Compare commits
10 Commits
260b014a84
...
5ce62f0bb5
Author | SHA1 | Date | |
---|---|---|---|
5ce62f0bb5 | |||
6d457c9ca2 | |||
3c6679032d | |||
34bc37e864 | |||
142c31a322 | |||
9fcd390366 | |||
5737e415c9 | |||
590970b0b3 | |||
ad74a7ca7d | |||
5705ab962a |
3
.gitignore
vendored
3
.gitignore
vendored
@ -103,3 +103,6 @@ DerivedDataCache/*
|
||||
/TestForAIGC/Plugins/HttpHelper/Binaries
|
||||
/TestForAIGC/Plugins/HttpHelper/Content
|
||||
/AIGC/aigc.log
|
||||
/TestForAIGC/TestForAIGC.uproject.DotSettings.user
|
||||
/AIGC/__pycache__/AICore.cpython-312.pyc
|
||||
/AIGC/__pycache__/DatabaseHandle.cpython-312.pyc
|
||||
|
21
AIGC/AICore.py
Normal file
21
AIGC/AICore.py
Normal file
@ -0,0 +1,21 @@
|
||||
import requests
|
||||
from ollama import Client, ResponseError
|
||||
import tiktoken
|
||||
|
||||
class AICore:
|
||||
modelMaxTokens = 128000
|
||||
# 初始化 DeepSeek 使用的 Tokenizer (cl100k_base)
|
||||
encoder = tiktoken.get_encoding("cl100k_base")
|
||||
|
||||
|
||||
|
||||
def __init__(self, model):
|
||||
#初始化ollama客户端
|
||||
self.ollamaClient = Client(host='http://localhost:11434', headers={'x-some-header': 'some-value'})
|
||||
response = self.ollamaClient.show(model)
|
||||
modelMaxTokens = response.modelinfo['qwen2.context_length']
|
||||
|
||||
def getPromptToken(self, prompt)-> int:
|
||||
tokens = self.encoder.encode(prompt)
|
||||
return len(tokens)
|
||||
|
119
AIGC/DatabaseHandle.py
Normal file
119
AIGC/DatabaseHandle.py
Normal file
@ -0,0 +1,119 @@
|
||||
from contextlib import contextmanager
|
||||
import sqlite3
|
||||
|
||||
class DatabaseHandle:
|
||||
def __init__(self, db_path = "data.db"):
|
||||
self.db_path = db_path
|
||||
self._init_db()
|
||||
|
||||
def _init_db(self):
|
||||
"""创建表"""
|
||||
with self._get_connection() as conn:
|
||||
#创建角色表
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS characters
|
||||
(id INTEGER PRIMARY KEY,
|
||||
name TEXT not NULL UNIQUE,
|
||||
age INTEGER,
|
||||
personality TEXT,
|
||||
profession TEXT,
|
||||
characterBackground TEXT,
|
||||
chat_style TEXT
|
||||
)
|
||||
''')
|
||||
#创建聊天记录表
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS chat_records
|
||||
(id INTEGER PRIMARY KEY,
|
||||
character_ids TEXT not NULL,
|
||||
chat TEXT,
|
||||
time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
@contextmanager
|
||||
def _get_connection(self):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
try:
|
||||
yield conn
|
||||
conn.commit()
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def add_character(self, data:dict):
|
||||
"""添加角色数据"""
|
||||
with self._get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO characters (
|
||||
name, age, personality, profession, characterBackground,
|
||||
chat_style
|
||||
) VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(name) DO UPDATE SET
|
||||
age = excluded.age,
|
||||
personality = excluded.personality,
|
||||
profession = excluded.profession,
|
||||
characterBackground = excluded.characterBackground,
|
||||
chat_style = excluded.chat_style
|
||||
''', (
|
||||
data["name"], data["age"], data["personality"],
|
||||
data["profession"], data["characterBackground"],
|
||||
data["chat_style"]
|
||||
))
|
||||
conn.commit()
|
||||
return cursor.lastrowid
|
||||
|
||||
def get_character_byname(self, name:str) ->list:
|
||||
"""
|
||||
根据角色名称查询数据(name为空时返回全部数据)
|
||||
:param name: 角色名称(精确匹配,None 或空字符串时返回全部)
|
||||
:return: 角色数据字典列表
|
||||
"""
|
||||
with self._get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 动态构建SQL语句
|
||||
sql = "SELECT * FROM characters"
|
||||
params = ()
|
||||
|
||||
if name: # 当name非空时添加条件
|
||||
sql += " WHERE name = ?"
|
||||
params = (name,)
|
||||
|
||||
cursor.execute(sql, params)
|
||||
|
||||
# 转换结果为字典列表
|
||||
columns = [col[0] for col in cursor.description]
|
||||
return [dict(zip(columns, row)) for row in cursor.fetchall()]
|
||||
|
||||
def add_chat(self, data:dict):
|
||||
"""添加聊天数据"""
|
||||
with self._get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO chat_records (
|
||||
character_ids, chat
|
||||
) VALUES (?, ?)
|
||||
''', (
|
||||
data["character_ids"], data["chat"]
|
||||
))
|
||||
conn.commit()
|
||||
return cursor.lastrowid
|
||||
|
||||
def get_chats_by_character_id(self, character_id: int) -> list:
|
||||
"""
|
||||
根据角色ID查询聊天记录(target_id为空时返回全部数据)
|
||||
:param target_id: 目标角色ID(None时返回全部记录)
|
||||
:return: 聊天记录字典列表
|
||||
"""
|
||||
with self._get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
sql = "SELECT * FROM chat_records WHERE ',' || character_ids || ',' LIKE '%,' || ? || ',%'"
|
||||
params = (str(character_id))
|
||||
cursor.execute(sql, params)
|
||||
# 转换结果为字典列表
|
||||
columns = [col[0] for col in cursor.description]
|
||||
return [dict(zip(columns, row)) for row in cursor.fetchall()]
|
BIN
AIGC/data.db
Normal file
BIN
AIGC/data.db
Normal file
Binary file not shown.
156
AIGC/main.py
156
AIGC/main.py
@ -11,8 +11,10 @@ from fastapi import FastAPI, Request, HTTPException, WebSocket, WebSocketDisconn
|
||||
from fastapi.websockets import WebSocketState
|
||||
from h11 import ConnectionClosed
|
||||
import uvicorn
|
||||
from AICore import AICore
|
||||
from DatabaseHandle import DatabaseHandle
|
||||
from Utils.AIGCLog import AIGCLog
|
||||
from ollama import Client, ResponseError
|
||||
|
||||
|
||||
app = FastAPI(title = "AI 通信服务")
|
||||
logger = AIGCLog(name = "AIGC", log_file = "aigc.log")
|
||||
@ -27,8 +29,8 @@ logger.log(logging.INFO, f"使用的模型是 {args.model}")
|
||||
maxAIRegerateCount = 5
|
||||
lastPrompt = ""
|
||||
|
||||
#初始化ollama客户端
|
||||
ollamaClient = Client(host='http://localhost:11434')
|
||||
aicore = AICore(args.model)
|
||||
|
||||
|
||||
async def heartbeat(websocket: WebSocket):
|
||||
pass
|
||||
@ -40,14 +42,10 @@ async def heartbeat(websocket: WebSocket):
|
||||
# break # 连接已关闭时退出循环
|
||||
|
||||
#statuscode -1 服务器运行错误 0 心跳标志 1 正常 2 输出异常
|
||||
async def senddata(websocket: WebSocket, statusCode: int, messages: List[str]):
|
||||
async def senddata(websocket: WebSocket, protocol: dict):
|
||||
# 将AI响应发送回UE5
|
||||
if websocket.client_state == WebSocketState.CONNECTED:
|
||||
data = {
|
||||
"statuscode": statusCode,
|
||||
"messages": messages
|
||||
}
|
||||
json_string = json.dumps(data, ensure_ascii=False)
|
||||
json_string = json.dumps(protocol, ensure_ascii=False)
|
||||
await websocket.send_text(json_string)
|
||||
|
||||
# WebSocket路由处理
|
||||
@ -63,15 +61,17 @@ async def websocket_endpoint(websocket: WebSocket, client_id: str):
|
||||
# 接收UE5发来的消息
|
||||
data = await websocket.receive_text()
|
||||
logger.log(logging.INFO, f"收到UE5消息 [{client_id}]: {data}")
|
||||
success, prompt = process_prompt(data)
|
||||
global lastPrompt
|
||||
lastPrompt = prompt
|
||||
# 调用AI生成响应
|
||||
if(success):
|
||||
asyncio.create_task(generateAIChat(prompt, websocket))
|
||||
await senddata(websocket, 0, [])
|
||||
else:
|
||||
await senddata(websocket, -1, [])
|
||||
await process_protocol_json(data, websocket)
|
||||
|
||||
# success, prompt = process_prompt(data)
|
||||
# global lastPrompt
|
||||
# lastPrompt = prompt
|
||||
# # 调用AI生成响应
|
||||
# if(success):
|
||||
# asyncio.create_task(generateAIChat(prompt, websocket))
|
||||
# await senddata(websocket, 0, [])
|
||||
# else:
|
||||
# await senddata(websocket, -1, [])
|
||||
|
||||
|
||||
|
||||
@ -83,6 +83,41 @@ async def websocket_endpoint(websocket: WebSocket, client_id: str):
|
||||
#manager.disconnect(client_id)
|
||||
logger.log(logging.ERROR, f"WebSocket异常 [{client_id}]: {str(e)}")
|
||||
|
||||
async def handle_characterlist(client: WebSocket):
|
||||
### 获得数据库中的角色信息###
|
||||
characters = database.get_character_byname("")
|
||||
protocol = {}
|
||||
protocol["cmd"] = "CharacterList"
|
||||
protocol["status"] = 1
|
||||
protocol["message"] = "success"
|
||||
characterforUE = {}
|
||||
characterforUE["characterInfos"] = characters
|
||||
protocol["data"] = json.dumps(characterforUE)
|
||||
await senddata(client, protocol)
|
||||
|
||||
async def handle_addcharacter(client: WebSocket, chracterJson: str):
|
||||
### 添加角色到数据库 ###
|
||||
character_info = json.loads(chracterJson)
|
||||
id = database.add_character(character_info)
|
||||
logger.log(logging.INFO, f"添加角色到数据库 id = {id}")
|
||||
# id = database.add_character({"name":"张三","age":35,"personality":"成熟稳重/惜字如金","profession":"阿里巴巴算法工程师"
|
||||
# ,"characterBackground":"浙大计算机系毕业,专注AI优化项目","chat_style":"请在对话中表现出专业、冷静、惜字如金。用口语化的方式简短回答"})
|
||||
|
||||
async def process_protocol_json(json_str: str, client: WebSocket):
|
||||
### 处理协议JSON ###
|
||||
try:
|
||||
protocol = json.loads(json_str)
|
||||
cmd = protocol.get("cmd")
|
||||
data = protocol.get("data")
|
||||
if cmd == "CharacterList":
|
||||
await handle_characterlist(client)
|
||||
elif cmd == "AddCharacter":
|
||||
await handle_addcharacter(client, data)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"JSON解析错误: {e}")
|
||||
|
||||
|
||||
def process_prompt(promptFromUE: str) -> Tuple[bool, str]:
|
||||
try:
|
||||
data = json.loads(promptFromUE)
|
||||
@ -150,6 +185,7 @@ async def generateAIChat(promptStr: str, websocket: WebSocket| None = None):
|
||||
{"role": "system", "content": promptStr}
|
||||
]
|
||||
try:
|
||||
|
||||
# response = ollamaClient.chat(
|
||||
# model = args.model,
|
||||
# stream = False,
|
||||
@ -234,43 +270,57 @@ if __name__ == "__main__":
|
||||
server_thread.daemon = True # 设为守护线程(主程序退出时自动终止)
|
||||
server_thread.start()
|
||||
|
||||
# Test
|
||||
asyncio.run(
|
||||
generateAIChat(promptStr = f"""
|
||||
#你是一个游戏NPC对话生成器。请严格按以下要求生成两个角色的日常对话
|
||||
#对话的世界观背景是2025年的都市背景
|
||||
1. 生成【2轮完整对话】,每轮包含双方各一次发言(共4句)
|
||||
2.角色设定
|
||||
|
||||
#Test database
|
||||
database = DatabaseHandle()
|
||||
|
||||
|
||||
id = database.add_character({"name":"李明","age":30,"personality":"活泼健谈","profession":"产品经理"
|
||||
,"characterBackground":"公司资深产品经理","chat_style":"热情"})
|
||||
|
||||
characters = database.get_character_byname("")
|
||||
#chat_id = database.add_chat({"character_ids":"1,2","chat":"张三:[第一轮发言] 李明:[第一轮回应] 张三:[第二轮发言] 李明:[第二轮回应"})
|
||||
chat = database.get_chats_by_character_id(3)
|
||||
if id == 0:
|
||||
logger.log(logging.ERROR, f"角色 张三已经添加到数据库")
|
||||
# Test AI
|
||||
aicore.getPromptToken("测试功能")
|
||||
# asyncio.run(
|
||||
# generateAIChat(promptStr = f"""
|
||||
# #你是一个游戏NPC对话生成器。请严格按以下要求生成两个角色的日常对话
|
||||
# #对话的世界观背景是2025年的都市背景
|
||||
# 1. 生成【2轮完整对话】,每轮包含双方各一次发言(共4句)
|
||||
# 2.角色设定
|
||||
|
||||
"张三": {{
|
||||
"姓名": "张三",
|
||||
"年龄": 35,
|
||||
"性格": "成熟稳重/惜字如金",
|
||||
"职业": "阿里巴巴算法工程师",
|
||||
"背景": "浙大计算机系毕业,专注AI优化项目",
|
||||
"对话场景": "你正在和用户聊天,用户是你的同事",
|
||||
"语言风格": "请在对话中表现出专业、冷静、惜字如金。用口语化的方式简短回答"
|
||||
}},
|
||||
"李明": {{
|
||||
"姓名": "李明",
|
||||
"年龄": 30,
|
||||
"职业": "产品经理",
|
||||
"性格": "活泼健谈"
|
||||
"背景": "公司资深产品经理",
|
||||
"对话场景": "你正在和用户聊天,用户是你的同事",
|
||||
"语言风格": "热情"
|
||||
}}
|
||||
# "张三": {{
|
||||
# "姓名": "张三",
|
||||
# "年龄": 35,
|
||||
# "性格": "成熟稳重/惜字如金",
|
||||
# "职业": "阿里巴巴算法工程师",
|
||||
# "背景": "浙大计算机系毕业,专注AI优化项目",
|
||||
# "对话场景": "你正在和用户聊天,用户是你的同事",
|
||||
# "语言风格": "请在对话中表现出专业、冷静、惜字如金。用口语化的方式简短回答"
|
||||
# }},
|
||||
# "李明": {{
|
||||
# "姓名": "李明",
|
||||
# "年龄": 30,
|
||||
# "职业": "产品经理",
|
||||
# "性格": "活泼健谈"
|
||||
# "背景": "公司资深产品经理",
|
||||
# "对话场景": "你正在和用户聊天,用户是你的同事",
|
||||
# "语言风格": "热情"
|
||||
# }}
|
||||
|
||||
3.输出格式:
|
||||
<format>
|
||||
张三:[第一轮发言]
|
||||
李明:[第一轮回应]
|
||||
张三:[第二轮发言]
|
||||
李明:[第二轮回应]
|
||||
</format>
|
||||
"""
|
||||
)
|
||||
)
|
||||
# 3.输出格式:
|
||||
# <format>
|
||||
# 张三:[第一轮发言]
|
||||
# 李明:[第一轮回应]
|
||||
# 张三:[第二轮发言]
|
||||
# 李明:[第二轮回应]
|
||||
# </format>
|
||||
# """
|
||||
# )
|
||||
# )
|
||||
|
||||
try:
|
||||
# 主线程永久挂起(监听退出信号)
|
||||
|
@ -1,3 +1,4 @@
|
||||
uvicorn[standard]
|
||||
fastapi
|
||||
ollama
|
||||
ollama
|
||||
tiktoken
|
2
AIGC/数据库工具/SQLiteStudio/qt.conf
Normal file
2
AIGC/数据库工具/SQLiteStudio/qt.conf
Normal file
@ -0,0 +1,2 @@
|
||||
[Paths]
|
||||
Prefix=..
|
5
TestForAIGC/Config/DefaultAIGCSetting.ini
Normal file
5
TestForAIGC/Config/DefaultAIGCSetting.ini
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
[/Script/AIGC.AIGCSetting]
|
||||
ServerIP=127.0.0.1
|
||||
|
Binary file not shown.
5
TestForAIGC/Plugins/AIGC/Source/AIGC/Definations.cpp
Normal file
5
TestForAIGC/Plugins/AIGC/Source/AIGC/Definations.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
#include "Definations.h"
|
||||
|
||||
FString FNetCommand::CharacterList = TEXT("CharacterList");
|
||||
FString FNetCommand::AddCharacter = TEXT("AddCharacter");
|
||||
FString FNetCommand::AiChatGenerate = TEXT("AiChatGenerate");
|
@ -2,42 +2,34 @@
|
||||
|
||||
#include "AIGC.h"
|
||||
|
||||
#include "AIGCSetting.h"
|
||||
#include "AIGCStyle.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "FileHelpers.h"
|
||||
#include "ISettingsModule.h"
|
||||
#include "LevelEditor.h"
|
||||
#include "ToolMenu.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "WebSocketManager.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
#include "UObject/SavePackage.h"
|
||||
#include "Widget/AIGCWindow.h"
|
||||
#include "Widget/CharacterWindow.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FAIGCModule"
|
||||
#define AIGCWindowName FName("AIGC")
|
||||
#define CharacterWindowName FName("Character")
|
||||
|
||||
|
||||
|
||||
void FAIGCModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
|
||||
RegeisterSettings();
|
||||
//添加style
|
||||
FAIGCStyle::Initialize();
|
||||
FAIGCStyle::ReloadTextures();
|
||||
// //注册Command
|
||||
// FAIGCCommands::Register();
|
||||
// PluginCommands = MakeShareable(new FUICommandList);
|
||||
// //绑定动作
|
||||
// PluginCommands->MapAction(
|
||||
// FAIGCCommands::Get().MenuAction,
|
||||
// FExecuteAction::CreateRaw(this, &FAIGCModule::MenuClicked),
|
||||
// FCanExecuteAction());
|
||||
//
|
||||
// PluginCommands->MapAction(
|
||||
// FAIGCCommands::Get().SubMenuAction,
|
||||
// FExecuteAction::CreateRaw(this, &FAIGCModule::SubMenuClicked),
|
||||
// FCanExecuteAction());
|
||||
//添加菜单
|
||||
UToolMenu* menu = UToolMenus::Get()->FindMenu(FName("LevelEditor.MainMenu.AIGC"));
|
||||
if (!menu)
|
||||
@ -62,16 +54,17 @@ void FAIGCModule::StartupModule()
|
||||
FGlobalTabmanager::Get()->TryInvokeTab(AIGCWindowName);
|
||||
})));
|
||||
|
||||
// // 填充第二个子菜单项
|
||||
// FToolMenuSection& SubSection2 = SubMenu->AddSection(
|
||||
// "SubSection2");
|
||||
// SubSection.AddMenuEntry(
|
||||
// "Submenu2",
|
||||
// LOCTEXT("Action1Label", "生成模型2"),
|
||||
// LOCTEXT("Action1Tooltip", "触发模型生成2"),
|
||||
// FSlateIcon(FAIGCStyle::GetStyleSetName(), "AIGCStyle.AIChat"),
|
||||
// FToolUIActionChoice(
|
||||
// FAIGCCommands::Get().SubMenuAction, *PluginCommands));
|
||||
//添加人员配置菜单
|
||||
FToolMenuSection& CharacterSection = SubMenu->AddSection("Character");
|
||||
SubSection.AddMenuEntry(
|
||||
"Character",
|
||||
LOCTEXT("Character", "角色配置"),
|
||||
LOCTEXT("CharacterToolTip", ""),
|
||||
FSlateIcon(FAIGCStyle::GetStyleSetName(), "AIGCStyle.AIChat"),
|
||||
FUIAction(FExecuteAction::CreateLambda([this]()
|
||||
{
|
||||
FGlobalTabmanager::Get()->TryInvokeTab(CharacterWindowName);
|
||||
})));
|
||||
}),
|
||||
false, // 是否动态生成
|
||||
FSlateIcon());
|
||||
@ -81,42 +74,54 @@ void FAIGCModule::StartupModule()
|
||||
|
||||
}
|
||||
|
||||
//注册窗口
|
||||
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(AIGCWindowName, FOnSpawnTab::CreateRaw(this, &FAIGCModule::OnSpawnPluginTab))
|
||||
//注册Chat窗口
|
||||
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(AIGCWindowName, FOnSpawnTab::CreateRaw(this, &FAIGCModule::OnSpawnAIChatTab))
|
||||
.SetDisplayName(LOCTEXT("AIGCWindow", "AIGCWindow"))
|
||||
.SetMenuType(ETabSpawnerMenuType::Hidden);
|
||||
//注册角色窗口
|
||||
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(CharacterWindowName, FOnSpawnTab::CreateRaw(this, &FAIGCModule::OnSpawnCharacterTab))
|
||||
.SetDisplayName(LOCTEXT("CharacterWindow", "CharacterWindow"))
|
||||
.SetMenuType(ETabSpawnerMenuType::Hidden);
|
||||
}
|
||||
|
||||
|
||||
// void FAIGCModule::SubMenuClicked()
|
||||
// {
|
||||
// UE_LOG(LogTemp, Warning, TEXT("Generate Model"));
|
||||
// FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("4444555"));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
void FAIGCModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
UnregisterSettings();
|
||||
UToolMenus::UnregisterOwner(this);
|
||||
FAIGCStyle::Shutdown();
|
||||
//FAIGCCommands::Unregister();
|
||||
|
||||
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(AIGCWindowName);
|
||||
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(CharacterWindowName);
|
||||
}
|
||||
|
||||
void FAIGCModule::RegeisterSettings()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||
if (!SettingsModule)
|
||||
return;
|
||||
|
||||
const auto Section = SettingsModule->RegisterSettings("Project", "Plugins", "AIGC",
|
||||
FText::FromString(TEXT("AIGC")),
|
||||
FText::FromString(TEXT("AIGC Setting")),
|
||||
GetMutableDefault<UAIGCSetting>());
|
||||
#endif
|
||||
}
|
||||
|
||||
void FAIGCModule::UnregisterSettings()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||
if (SettingsModule)
|
||||
SettingsModule->UnregisterSettings("Project", "Plugins", "AIGC");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
TSharedRef<SDockTab> FAIGCModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
|
||||
TSharedRef<SDockTab> FAIGCModule::OnSpawnAIChatTab(const FSpawnTabArgs& SpawnTabArgs)
|
||||
{
|
||||
FText WidgetText = FText::Format(
|
||||
LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"),
|
||||
FText::FromString(TEXT("FTestModule::OnSpawnPluginTab")),
|
||||
FText::FromString(TEXT("Test.cpp"))
|
||||
);
|
||||
|
||||
return SNew(SDockTab)
|
||||
.TabRole(ETabRole::NomadTab)
|
||||
[
|
||||
@ -130,6 +135,21 @@ TSharedRef<SDockTab> FAIGCModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTab
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FAIGCModule::OnSpawnCharacterTab(const FSpawnTabArgs& SpawnTabArgs)
|
||||
{
|
||||
return SNew(SDockTab)
|
||||
.TabRole(ETabRole::NomadTab)
|
||||
[
|
||||
// Put your tab content here!
|
||||
SNew(SBox)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(SCharacterWindow)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
void FAIGCModule::CreateOrAddData(const FString& DataTablePath, const FString& RowValue)
|
||||
{
|
||||
FString PackagePath, AssetName;
|
||||
@ -158,6 +178,18 @@ void FAIGCModule::CreateOrAddData(const FString& DataTablePath, const FString& R
|
||||
UPackage::SavePackage(DataTable->GetPackage(), DataTable, *FilePath, SaveArgs);
|
||||
}
|
||||
|
||||
UWebSocketManager* FAIGCModule::GetWebSocketManager()
|
||||
{
|
||||
return WebSocketManager;
|
||||
}
|
||||
|
||||
void FAIGCModule::InitWebSocketManager()
|
||||
{
|
||||
auto& Settings = *GetDefault<UAIGCSetting>();
|
||||
WebSocketManager = NewObject<UWebSocketManager>();
|
||||
WebSocketManager->InitWebSocket(Settings.ServerIP);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FAIGCModule, AIGC)
|
@ -0,0 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "AIGCSetting.h"
|
@ -19,10 +19,16 @@ void UWebSocketManager::InitWebSocket(const FString& ServerIP)
|
||||
,*ServerIP, *(FGuid::NewGuid().ToString()) )));
|
||||
}
|
||||
|
||||
void UWebSocketManager::SendData(const FString& Data)
|
||||
void UWebSocketManager::SendData(const FString& cmd, const FString& Data)
|
||||
{
|
||||
//定义客户端协议
|
||||
FNetProtocol protocol;
|
||||
protocol.cmd = cmd;
|
||||
protocol.data = Data;
|
||||
FString JsonString;
|
||||
FJsonObjectConverter::UStructToJsonObjectString(protocol, JsonString);
|
||||
|
||||
WebSocketHandler->SendText(Data);
|
||||
WebSocketHandler->SendText(JsonString);
|
||||
}
|
||||
|
||||
void UWebSocketManager::HandleConnected()
|
||||
@ -52,30 +58,38 @@ void UWebSocketManager::HandleOnMessageSend(const FString& Message)
|
||||
void UWebSocketManager::HandleOnReceiveText(const FString& Message)
|
||||
{
|
||||
|
||||
FAIServerData ServerData;
|
||||
FJsonObjectConverter::JsonObjectStringToUStruct<FAIServerData>(
|
||||
// FAIServerData ServerData;
|
||||
// FJsonObjectConverter::JsonObjectStringToUStruct<FAIServerData>(
|
||||
// Message,
|
||||
// &ServerData,
|
||||
// 0, // 检查标志位
|
||||
// 0 // 转换标志位
|
||||
// );
|
||||
// switch (ServerData.statusCode)
|
||||
// {
|
||||
// case -1:
|
||||
// UE_LOG(LogTemp, Error, TEXT("ai server error reason = %s "), *ServerData.messages[0]);
|
||||
// OnDataReceiveDelaget.Broadcast(TArray<FString>());
|
||||
// break;
|
||||
// case 0:
|
||||
// //normal heart tick
|
||||
// break;
|
||||
// case 1:
|
||||
// UE_LOG(LogTemp, Warning, TEXT("Receive Text %s from WebSocket Server "), *Message);
|
||||
// OnDataReceiveDelaget.Broadcast(ServerData.messages);
|
||||
// break;
|
||||
// case 2:
|
||||
// UE_LOG(LogTemp, Error, TEXT("ai content generate format error,server is regerating..... "));
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
FNetProtocol serverProtocol;
|
||||
FJsonObjectConverter::JsonObjectStringToUStruct<FNetProtocol>(
|
||||
Message,
|
||||
&ServerData,
|
||||
&serverProtocol,
|
||||
0, // 检查标志位
|
||||
0 // 转换标志位
|
||||
);
|
||||
switch (ServerData.statusCode)
|
||||
{
|
||||
case -1:
|
||||
UE_LOG(LogTemp, Error, TEXT("ai server error reason = %s "), *ServerData.messages[0]);
|
||||
OnDataReceiveDelaget.Broadcast(TArray<FString>());
|
||||
break;
|
||||
case 0:
|
||||
//normal heart tick
|
||||
break;
|
||||
case 1:
|
||||
UE_LOG(LogTemp, Warning, TEXT("Receive Text %s from WebSocket Server "), *Message);
|
||||
OnDataReceiveDelaget.Broadcast(ServerData.messages);
|
||||
break;
|
||||
case 2:
|
||||
UE_LOG(LogTemp, Error, TEXT("ai content generate format error,server is regerating..... "));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
OnDataReceiveDelaget.Broadcast(serverProtocol);
|
||||
}
|
||||
|
@ -106,13 +106,23 @@ void SAIGCWindow::Construct(const FArguments& InArgs)
|
||||
|
||||
EActiveTimerReturnType SAIGCWindow::OnPostPaint(double X, float Arg)
|
||||
{
|
||||
WebSocketManager = NewObject<UWebSocketManager>();
|
||||
WebSocketManager->InitWebSocket(*ServerIP->GetInputText());
|
||||
WebSocketManager->OnConnectDelegate.AddLambda([this](bool bSuccess)
|
||||
//init websocket
|
||||
FAIGCModule* ModulePtr = FModuleManager::GetModulePtr<FAIGCModule>("AIGC");
|
||||
if (ModulePtr)
|
||||
{
|
||||
GenerateButton->SetEnabled(bSuccess);
|
||||
});
|
||||
WebSocketManager->OnDataReceiveDelaget.AddRaw(this, &SAIGCWindow::HandleReceiveData);
|
||||
UWebSocketManager* WebSocketManager = ModulePtr->GetWebSocketManager();
|
||||
if (!WebSocketManager)
|
||||
{
|
||||
ModulePtr->InitWebSocketManager();
|
||||
WebSocketManager = ModulePtr->GetWebSocketManager();
|
||||
}
|
||||
WebSocketManager->OnConnectDelegate.AddLambda([this](bool bSuccess)
|
||||
{
|
||||
GenerateButton->SetEnabled(bSuccess);
|
||||
});
|
||||
WebSocketManager->OnDataReceiveDelaget.AddRaw(this, &SAIGCWindow::HandleReceiveData);
|
||||
|
||||
}
|
||||
return EActiveTimerReturnType::Stop;
|
||||
}
|
||||
|
||||
@ -122,40 +132,50 @@ void SAIGCWindow::OnAIGenerateClicked()
|
||||
GenerateButton->SetEnabled(false);
|
||||
RequireGenerateCount = GenerateCount->GetNumber();
|
||||
UE_LOG(LogTemp, Warning, TEXT("生成次数 %d prompt:%s"), RequireGenerateCount, *GeneratePromptJson());
|
||||
WebSocketManager->SendData(GeneratePromptJson());
|
||||
FAIGCModule* ModulePtr = FModuleManager::GetModulePtr<FAIGCModule>("AIGC");
|
||||
if (ModulePtr)
|
||||
{
|
||||
ModulePtr->GetWebSocketManager()->SendData(FNetCommand::AiChatGenerate, GeneratePromptJson());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SAIGCWindow::HandleReceiveData(TArray<FString> AiMessages)
|
||||
void SAIGCWindow::HandleReceiveData(FNetProtocol protocol)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("AI当前进度 %d/%d"), GeneratedCount, RequireGenerateCount);
|
||||
//添加数据到dataTable中
|
||||
if (AiMessages.Num() > 0)
|
||||
{
|
||||
FAIGCModule* ModulePtr = FModuleManager::GetModulePtr<FAIGCModule>("AIGC");
|
||||
if (ModulePtr) {
|
||||
FString DataPath = FString::Printf(TEXT("/Game/Test/%s.%s"), *DataName->GetInputText(), *DataName->GetInputText());
|
||||
FString Final = "";
|
||||
for (auto message: AiMessages)
|
||||
{
|
||||
Final += message + TEXT("|");
|
||||
}
|
||||
ModulePtr->CreateOrAddData(DataPath, Final);
|
||||
}
|
||||
}
|
||||
|
||||
//是否生成足够数量
|
||||
if (GeneratedCount < RequireGenerateCount)
|
||||
{
|
||||
GeneratedCount++;
|
||||
WebSocketManager->SendData(GeneratePromptJson());
|
||||
}
|
||||
else
|
||||
{
|
||||
GeneratedCount = 0;
|
||||
GenerateButton->SetEnabled(true);
|
||||
UE_LOG(LogTemp, Warning, TEXT("生成结束!"));
|
||||
}
|
||||
// UE_LOG(LogTemp, Warning, TEXT("AI当前进度 %d/%d"), GeneratedCount, RequireGenerateCount);
|
||||
// //添加数据到dataTable中
|
||||
// if (AiMessages.Num() > 0)
|
||||
// {
|
||||
// FAIGCModule* ModulePtr = FModuleManager::GetModulePtr<FAIGCModule>("AIGC");
|
||||
// if (ModulePtr) {
|
||||
// FString DataPath = FString::Printf(TEXT("/Game/Test/%s.%s"), *DataName->GetInputText(), *DataName->GetInputText());
|
||||
// FString Final = "";
|
||||
// for (auto message: AiMessages)
|
||||
// {
|
||||
// Final += message + TEXT("|");
|
||||
// }
|
||||
// ModulePtr->CreateOrAddData(DataPath, Final);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //是否生成足够数量
|
||||
// if (GeneratedCount < RequireGenerateCount)
|
||||
// {
|
||||
// GeneratedCount++;
|
||||
// FAIGCModule* ModulePtr = FModuleManager::GetModulePtr<FAIGCModule>("AIGC");
|
||||
// if (ModulePtr)
|
||||
// {
|
||||
// ModulePtr->GetWebSocketManager()->SendData(FNetCommand::AiChatGenerate, GeneratePromptJson());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// GeneratedCount = 0;
|
||||
// GenerateButton->SetEnabled(true);
|
||||
// UE_LOG(LogTemp, Warning, TEXT("生成结束!"));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,187 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Widget/CharacterWindow.h"
|
||||
|
||||
#include "AIGC.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "WebSocketManager.h"
|
||||
#include "Widgets/Input/STextComboBox.h"
|
||||
#define LOCTEXT_NAMESPACE "FAIGCModule"
|
||||
|
||||
void SCharacterWindow::Construct(const FArguments& InArgs)
|
||||
{
|
||||
|
||||
|
||||
ChildSlot[
|
||||
SNew(SVerticalBox)
|
||||
+SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
//角色列表
|
||||
SAssignNew(CharacterComboBox, SComboBox<TSharedPtr<FCharacterInfo>>)
|
||||
.OptionsSource(&CharacterInfos)
|
||||
.Content()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() -> FText {
|
||||
|
||||
return FText::FromString(SelectedCharacter.name);
|
||||
})
|
||||
]
|
||||
.OnGenerateWidget_Lambda([this](TSharedPtr<FCharacterInfo> CharacterInfo) {
|
||||
return SNew(STextBlock).Text(FText::FromString(CharacterInfo->name));
|
||||
})
|
||||
.OnSelectionChanged_Lambda([this](TSharedPtr<FCharacterInfo> CharacterInfo, ESelectInfo::Type)
|
||||
{
|
||||
SelectedCharacter = *CharacterInfo;
|
||||
|
||||
this->CharacterWidget_Name->SetInputText(SelectedCharacter.name);
|
||||
this->CharacterWidget_Age->SetNumber(SelectedCharacter.age);
|
||||
this->CharacterWidget_Personality->SetInputText(SelectedCharacter.personality);
|
||||
this->CharacterWidget_Profession->SetInputText(SelectedCharacter.profession);
|
||||
this->CharacterWidget_BG->SetInputText(SelectedCharacter.characterBackground);
|
||||
this->CharacterWidget_Style->SetInputText(SelectedCharacter.chat_style);
|
||||
})
|
||||
]
|
||||
+SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SAssignNew(CharacterWidget_Name, SConfigItem_Text)
|
||||
.Title(LOCTEXT("CharacterName", "角色名称"))
|
||||
.HintText(LOCTEXT("HintCharacterName", "请输入角色名称"))
|
||||
]
|
||||
+SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SAssignNew(CharacterWidget_Age, SConfigItem_NumberSpin<int32>)
|
||||
.Title(LOCTEXT("CharacterAge", "角色年龄"))
|
||||
.MinNumber(1)
|
||||
.MaxNumber(99)
|
||||
]
|
||||
+SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SAssignNew(CharacterWidget_Personality, SConfigItem_Text)
|
||||
.Title(LOCTEXT("CharacterName", "角色性格"))
|
||||
.HintText(LOCTEXT("HintCharacterName", "请输入角色性格"))
|
||||
]
|
||||
+SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SAssignNew(CharacterWidget_Profession, SConfigItem_Text)
|
||||
.Title(LOCTEXT("CharacterName", "角色职业"))
|
||||
.HintText(LOCTEXT("HintCharacterName", "请输入角色职业"))
|
||||
]
|
||||
+SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SAssignNew(CharacterWidget_BG, SConfigItem_Text)
|
||||
.Title(LOCTEXT("CharacterName", "角色背景"))
|
||||
.HintText(LOCTEXT("HintCharacterName", "请输入角色背景"))
|
||||
]
|
||||
+SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SAssignNew(CharacterWidget_Style, SConfigItem_Text)
|
||||
.Title(LOCTEXT("CharacterName", "角色对话风格"))
|
||||
.HintText(LOCTEXT("HintCharacterName", "请输入角色对话风格"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SNew( SButton)
|
||||
.Text(LOCTEXT("AddCharacter", "添加角色到服务器中"))
|
||||
.OnClicked_Lambda([this]()
|
||||
{
|
||||
AddCharacterToServer();
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
];
|
||||
// 注册绘制后回调
|
||||
RegisterActiveTimer(0.f,
|
||||
FWidgetActiveTimerDelegate::CreateSP(
|
||||
this,
|
||||
&SCharacterWindow::OnPostPaint
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
EActiveTimerReturnType SCharacterWindow::OnPostPaint(double X, float Arg)
|
||||
{
|
||||
//init websocket
|
||||
FAIGCModule* ModulePtr = FModuleManager::GetModulePtr<FAIGCModule>("AIGC");
|
||||
if (ModulePtr)
|
||||
{
|
||||
UWebSocketManager* WebSocketManager = ModulePtr->GetWebSocketManager();
|
||||
if (!WebSocketManager)
|
||||
{
|
||||
ModulePtr->InitWebSocketManager();
|
||||
WebSocketManager = ModulePtr->GetWebSocketManager();
|
||||
}
|
||||
else
|
||||
{
|
||||
WebSocketManager->SendData(FNetCommand::CharacterList, TEXT(""));
|
||||
}
|
||||
WebSocketManager->OnDataReceiveDelaget.AddRaw(this, &SCharacterWindow::HandleReceiveData);
|
||||
WebSocketManager->OnConnectDelegate.AddLambda([this, WebSocketManager](bool bSuccess)
|
||||
{
|
||||
WebSocketManager->SendData(FNetCommand::CharacterList, TEXT(""));
|
||||
});
|
||||
}
|
||||
return EActiveTimerReturnType::Stop;
|
||||
}
|
||||
|
||||
void SCharacterWindow::HandleReceiveData(FNetProtocol protocol)
|
||||
{
|
||||
if (protocol.cmd == FNetCommand::CharacterList)
|
||||
{
|
||||
|
||||
//解析json角色信息
|
||||
FJsonObjectConverter::JsonObjectStringToUStruct<FCharacterArray>(
|
||||
protocol.data,
|
||||
&Characters,
|
||||
0, // 检查标志位
|
||||
0 // 转换标志位
|
||||
);
|
||||
CharacterInfos.Empty();
|
||||
for (auto Character: Characters.characterInfos)
|
||||
{
|
||||
CharacterInfos.Add(MakeShareable(new FCharacterInfo(Character)));
|
||||
}
|
||||
CharacterComboBox->RefreshOptions();
|
||||
}
|
||||
}
|
||||
|
||||
void SCharacterWindow::AddCharacterToServer()
|
||||
{
|
||||
FAIGCModule* ModulePtr = FModuleManager::GetModulePtr<FAIGCModule>("AIGC");
|
||||
if (ModulePtr)
|
||||
{
|
||||
//生成角色信息
|
||||
FCharacterInfo CharacterInfo;
|
||||
CharacterInfo.name = CharacterWidget_Name->GetInputText();
|
||||
CharacterInfo.age = CharacterWidget_Age->GetNumber();
|
||||
CharacterInfo.personality = CharacterWidget_Personality->GetInputText();
|
||||
CharacterInfo.profession = CharacterWidget_Profession->GetInputText();
|
||||
CharacterInfo.characterBackground = CharacterWidget_BG->GetInputText();
|
||||
CharacterInfo.chat_style = CharacterWidget_Style->GetInputText();
|
||||
|
||||
FString CharacterJson;
|
||||
FJsonObjectConverter::UStructToJsonObjectString(CharacterInfo, CharacterJson);
|
||||
|
||||
UWebSocketManager* WebSocketManager = ModulePtr->GetWebSocketManager();
|
||||
WebSocketManager->SendData(FNetCommand::AddCharacter, CharacterJson);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -23,6 +23,7 @@ void SConfigItem_Text::Construct(const FArguments& InArgs)
|
||||
[
|
||||
SNew(SEditableTextBox)
|
||||
.HintText( InArgs._HintText)
|
||||
.VirtualKeyboardType(InArgs._KeyboardType)
|
||||
.Text_Lambda([this, InArgs]() {
|
||||
return FText::FromString(InputText);
|
||||
})
|
||||
|
@ -37,13 +37,22 @@ struct FNPCChatRow : public FTableRowBase {
|
||||
class FAIGCModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
TSharedRef<SDockTab> OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs);
|
||||
TSharedRef<SDockTab> OnSpawnAIChatTab(const FSpawnTabArgs& SpawnTabArgs);
|
||||
TSharedRef<SDockTab> OnSpawnCharacterTab(const FSpawnTabArgs& SpawnTabArgs);
|
||||
//void SubMenuClicked();
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
void RegeisterSettings();
|
||||
void UnregisterSettings();
|
||||
|
||||
void CreateOrAddData(const FString& DataTablePath, const FString& RowValue);
|
||||
class UWebSocketManager* GetWebSocketManager();
|
||||
void InitWebSocketManager();
|
||||
|
||||
private:
|
||||
TSharedPtr<class FUICommandList> PluginCommands;
|
||||
class UWebSocketManager* WebSocketManager = nullptr;
|
||||
};
|
||||
#undef LOCTEXT_NAMESPACE
|
19
TestForAIGC/Plugins/AIGC/Source/AIGC/Public/AIGCSetting.h
Normal file
19
TestForAIGC/Plugins/AIGC/Source/AIGC/Public/AIGCSetting.h
Normal file
@ -0,0 +1,19 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "AIGCSetting.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Config=AIGCSetting, DefaultConfig)
|
||||
class AIGC_API UAIGCSetting : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(Config, EditAnywhere)
|
||||
FString ServerIP = TEXT("127.0.0.1");
|
||||
};
|
@ -45,4 +45,54 @@ struct FAIServerData
|
||||
int32 statusCode;
|
||||
UPROPERTY()
|
||||
TArray<FString> messages;
|
||||
};
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FCharacterInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
UPROPERTY()
|
||||
int32 id;
|
||||
UPROPERTY()
|
||||
FString name;
|
||||
UPROPERTY()
|
||||
int32 age;
|
||||
UPROPERTY()
|
||||
FString personality;
|
||||
UPROPERTY()
|
||||
FString profession;
|
||||
UPROPERTY()
|
||||
FString characterBackground;
|
||||
UPROPERTY()
|
||||
FString chat_style;
|
||||
};
|
||||
USTRUCT()
|
||||
struct FCharacterArray
|
||||
{
|
||||
GENERATED_BODY()
|
||||
UPROPERTY()
|
||||
TArray<FCharacterInfo> characterInfos;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FNetProtocol
|
||||
{
|
||||
GENERATED_BODY()
|
||||
UPROPERTY()
|
||||
FString cmd; //命令名称
|
||||
UPROPERTY()
|
||||
int32 status; //状态
|
||||
UPROPERTY()
|
||||
FString message; //响应消息
|
||||
UPROPERTY()
|
||||
FString data; //json数据
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FNetCommand
|
||||
{
|
||||
GENERATED_BODY()
|
||||
static FString CharacterList;
|
||||
static FString AddCharacter;
|
||||
static FString AiChatGenerate;
|
||||
};
|
||||
|
@ -3,11 +3,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Definations.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "WebSocketManager.generated.h"
|
||||
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOnConnectDelegate, bool);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOnDataReceiveDelaget, TArray<FString>);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOnDataReceiveDelaget, FNetProtocol);
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -17,7 +18,7 @@ class AIGC_API UWebSocketManager : public UObject
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
void InitWebSocket(const FString& ServerIP);
|
||||
void SendData(const FString& Data);
|
||||
void SendData(const FString& cmd, const FString& Data);
|
||||
UFUNCTION()
|
||||
void HandleConnected();
|
||||
UFUNCTION()
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "ConfigItem_NumberSpin.h"
|
||||
#include "ConfigItem_Text.h"
|
||||
#include "Definations.h"
|
||||
|
||||
/**
|
||||
*
|
||||
@ -22,7 +23,7 @@ public:
|
||||
void OnAIGenerateClicked();
|
||||
|
||||
void InitWebClient();
|
||||
void HandleReceiveData(TArray<FString> String);
|
||||
void HandleReceiveData(FNetProtocol protocol);
|
||||
|
||||
private:
|
||||
FString GeneratePromptJson();
|
||||
@ -39,6 +40,5 @@ protected:
|
||||
TSharedPtr<SConfigItem_NumberSpin<int32>> GenerateCount; //生成数目
|
||||
TSharedPtr<SButton> GenerateButton; //生成按钮
|
||||
|
||||
UPROPERTY()
|
||||
class UWebSocketManager* WebSocketManager;
|
||||
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ConfigItem_NumberSpin.h"
|
||||
#include "ConfigItem_Text.h"
|
||||
#include "Definations.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class AIGC_API SCharacterWindow: public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SCharacterWindow)
|
||||
{}
|
||||
|
||||
SLATE_END_ARGS()
|
||||
void Construct(const FArguments& InArgs);
|
||||
EActiveTimerReturnType OnPostPaint(double X, float Arg);
|
||||
|
||||
void HandleReceiveData(FNetProtocol protocol);
|
||||
void AddCharacterToServer();
|
||||
protected:
|
||||
TSharedPtr<SComboBox<TSharedPtr<FCharacterInfo>>> CharacterComboBox;
|
||||
TSharedPtr<SConfigItem_Text> CharacterWidget_Name; //角色名称
|
||||
TSharedPtr<SConfigItem_NumberSpin<int32>> CharacterWidget_Age; //角色年龄
|
||||
TSharedPtr<SConfigItem_Text> CharacterWidget_Personality; //角色性格
|
||||
TSharedPtr<SConfigItem_Text> CharacterWidget_Profession; //角色专业
|
||||
TSharedPtr<SConfigItem_Text> CharacterWidget_BG; //角色背景
|
||||
TSharedPtr<SConfigItem_Text> CharacterWidget_Style; //说话风格
|
||||
|
||||
|
||||
FCharacterArray Characters;
|
||||
TArray<TSharedPtr<FCharacterInfo>> CharacterInfos;
|
||||
FCharacterInfo SelectedCharacter;
|
||||
};
|
||||
|
||||
|
@ -53,11 +53,10 @@ public:
|
||||
];
|
||||
}
|
||||
NumericType GetNumber() const { return InputValue; }
|
||||
|
||||
void SetNumber(NumericType InValue) { InputValue = InValue; }
|
||||
private:
|
||||
void OnValueChanged(NumericType NewValue)
|
||||
{
|
||||
UE_LOG(LogTemp, Display, TEXT("OnValueChanged newvalue = %d"), (int32)NewValue);
|
||||
InputValue = NewValue;
|
||||
Invalidate(EInvalidateWidget::Layout);
|
||||
}
|
||||
|
@ -13,17 +13,18 @@ public:
|
||||
: _Title()
|
||||
, _HintText()
|
||||
, _DefaultText()
|
||||
,_KeyboardType(EKeyboardType::Keyboard_Default)
|
||||
{}
|
||||
SLATE_ARGUMENT(FText, Title) // 标题参数
|
||||
SLATE_ARGUMENT(FText, HintText) // 默认值参数
|
||||
SLATE_ARGUMENT(FText, DefaultText) // 默认值参数
|
||||
|
||||
SLATE_ARGUMENT(EKeyboardType, KeyboardType)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
FString GetInputText() const { return InputText; }
|
||||
|
||||
void SetInputText(const FString& NewText) { InputText = NewText; }
|
||||
private:
|
||||
FString InputText;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user