初始化仅前端版本

This commit is contained in:
2026-03-12 18:41:55 +08:00
parent 741615ac3b
commit 43adf1a7c4
6 changed files with 238 additions and 29 deletions

46
backend/app/core/Start.py Normal file
View File

@@ -0,0 +1,46 @@
import base64
from typing import Any, Dict
from IPython.core.magic_arguments import defaults
from .. import nodes
class StartNode():
name = "开始节点"
inputs = {
"user_input": "string", # 用户输入文本
"stream": "boolean", # 是否流式输出
"img_switch": "boolean", # 是否处理图片
"table_switch": "boolean", # 是否处理表格
"role_name": "string", # 角色名称
"chat_name": "string" # 会话名称
}
async def run(self, text: str = None, image: bytes = None, **kwargs) -> Dict[str, Any]:
# 查空:文本不能为空字符串
if not text or text.strip() == "":
raise ValueError("文本输入不能为空")
# 查空:图片数据不能为空
if image is None or len(image) == 0:
raise ValueError("图片输入不能为空")
# 将图片字节转换为 Base64 字符串,便于在节点间传递
image_base64 = base64.b64encode(image).decode('utf-8')
return {
"text": text,
"image": image_base64
}
async def run(is_user,floor_number,mes: str = None, stream: bool = False, img_switch: bool = False,name = "default",
table_switch: bool = False, role_name: str = None, chat_name: str = None,preset: str = None):
# 将输入内容持久化存储到本地json方便前端读
nodes.save_input_to_json(mes=mes, role_name=role_name, chat_name=chat_name, name=name, is_user=is_user, floor_number=floor_number)
# 对上一条输入内容已确定不变的内容调用向量化根据role和chat嵌入到对应本地数据库
embed_input(user_input, role_name, chat_name)
# 根据role和chat去读取绑定的世界书
# 读取预设,进行拼接
# 调用模型,返回结果
# 将结果持久化存储到本地json方便前端读用JSONL
# 如果img_switch是开的那么异步调用生图并存储到目标文件夹里
# 如果table_switch是开的那么异步调用表格生成并存储到目标文件夹里
# 将结果返回给前端

View File

@@ -1,29 +0,0 @@
import base64
from core.node_base import BaseNode
from typing import Any, Dict
class StartNode(BaseNode):
name = "开始节点"
inputs = {} # 没有输入参数
outputs = {
"text": "string", # 文本内容
"image": "string" # 图片以 Base64 编码的字符串传递
}
async def run(self, text: str = None, image: bytes = None, **kwargs) -> Dict[str, Any]:
# 查空:文本不能为空字符串
if not text or text.strip() == "":
raise ValueError("文本输入不能为空")
# 查空:图片数据不能为空
if image is None or len(image) == 0:
raise ValueError("图片输入不能为空")
# 将图片字节转换为 Base64 字符串,便于在节点间传递
image_base64 = base64.b64encode(image).decode('utf-8')
return {
"text": text,
"image": image_base64
}

View File

@@ -0,0 +1,145 @@
import json
from typing import Dict, Any
from datetime import datetime
import config as cfg
from pathlib import Path
def save_input_to_json(
mes: str,
role_name: str,
chat_name: str,
name: str,
is_user: bool,
floor_number: int = 0
) -> Dict[str, Any]:
"""
保存消息到JSONL文件或处理重roll请求
参数:
mes: 消息内容
role_name: 角色名称
chat_name: 对话名称
name: 发送者名称
is_user: 是否为用户消息
floor_number: 楼层号(对话中的第几次回复)用于判断是否为重roll请求
返回:
更新后的消息对象
"""
config = cfg.settings
file_path = config.BASE_PATH / "data" / "chat" / role_name / f"{chat_name}.jsonl"
# 确保目录存在
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
# 读取文件内容
try:
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
except FileNotFoundError:
lines = []
# 判断是否为重roll请求
is_regenerate = False
target_index = -1
if lines and floor_number > 0:
# 计算当前楼层号
current_floor = len(lines)
# 如果floor_number与当前楼层号相同则为重roll请求
if floor_number == current_floor:
# 找到最后一条非用户消息
for i in range(len(lines) - 1, -1, -1):
try:
line_data = json.loads(lines[i])
if not line_data.get('is_user', False):
is_regenerate = True
target_index = i
break
except json.JSONDecodeError:
continue
# 处理重roll逻辑
if is_regenerate:
# 解析目标消息
try:
target_message = json.loads(lines[target_index])
except json.JSONDecodeError:
raise ValueError(f"无法解析楼层 {floor_number} 的JSON数据")
# 初始化swipes数组
if target_message.get('swipes') is None:
target_message['swipes'] = []
# 将新回复添加到swipes数组
target_message['swipes'].append(mes)
# 更新swipe_id和content
target_message['swipes_id'] = len(target_message['swipes']) - 1
target_message['content'] = mes
# 更新文件内容
lines[target_index] = json.dumps(target_message, ensure_ascii=False) + '\n'
# 写回文件
with open(file_path, 'w', encoding='utf-8') as f:
f.writelines(lines)
return target_message
# 处理普通消息保存逻辑
else:
# 获取当前时间
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 构建消息对象
message = {
"role": role_name,
"chat": chat_name,
"content": mes,
"name": name,
"is_user": is_user,
"send_date": current_time,
"floor_number": len(lines) + 1, # 记录楼层号
"swipes": [],
"swipes_id": 0
}
# 追加到文件
with open(file_path, 'a', encoding='utf-8') as f:
f.write(json.dumps(message, ensure_ascii=False) + '\n')
return message
if __name__ == '__main__':
# 测试普通消息保存
# save_input_to_json(
# mes="你好",
# role_name="test",
# chat_name="111",
# name="用户",
# is_user=True,
# floor_number=0
# )
#
# save_input_to_json(
# mes="你好我是AI助手",
# role_name="test",
# chat_name="111",
# name="AI",
# is_user=False,
# floor_number=1
# )
# 测试重roll最后一条AI消息
save_input_to_json(
mes="这是重roll后的新回复2",
role_name="test",
chat_name="111",
name="AI",
is_user=False,
floor_number=2 # 与当前楼层号相同表示重roll
)

View File

45
config.py Normal file
View File

@@ -0,0 +1,45 @@
import os
from pathlib import Path
from dotenv import load_dotenv
# 1. 动态计算项目根目录
# 假设 config.py 位于 backend/ 目录下
# __file__ 指向本文件的绝对路径
# .parent 指向 backend/ 目录
# .parent.parent 指向项目根目录 (即包含 backend/ 和 frontend/ 的目录)
PROJECT_ROOT = Path(__file__).resolve().parent
# 2. 加载 .env 文件
# 假设 .env 文件位于项目根目录下
load_dotenv(PROJECT_ROOT / ".env")
class Settings:
# --- 主模型配置 ---
MAIN_LLM_API_KEY = os.getenv("MAIN_LLM_API_KEY")
MAIN_LLM_MODEL = os.getenv("MAIN_LLM_MODEL", "gpt-3.5-turbo")
MAIN_LLM_BASE_URL = os.getenv("MAIN_LLM_BASE_URL", "https://api.openai.com/v1")
MAIN_LLM_MAX_TOKENS = int(os.getenv("MAIN_LLM_MAX_TOKENS", "4096"))
MAIN_LLM_STREAM = os.getenv("MAIN_LLM_STREAM", "true").lower() == "true"
# --- 路径配置 (核心修改) ---
# 强制使用计算出的项目根目录,不再依赖 .env 中的 BASE_PATH
BASE_PATH = PROJECT_ROOT
# 数据目录:固定为根目录下的 data 文件夹
# 即使 .env 里写了 DATA_PATH=/data这里也会强制指向项目根目录下的 data
DATA_PATH = BASE_PATH / "data"
# 其他文件路径:基于 DATA_PATH 拼接
STATE_FILE = DATA_PATH / "state.json"
SCHEMA_FILE = DATA_PATH / "schema.json"
PRESETS_FILE = DATA_PATH / "presets.json"
REGEX_FILE = DATA_PATH / "regex_rules.json"
VECTORSTORE_PATH = DATA_PATH / "vectorstore"
# ... 其他配置 ...
# 实例化配置对象
settings = Settings()

2
data/chat/test/111.jsonl Normal file
View File

@@ -0,0 +1,2 @@
{"role": "test", "chat": "111", "content": "你好", "name": "用户", "is_user": true, "send_date": "2026-03-12 18:26:50", "floor_number": 1, "swipes": [], "swipes_id": 0}
{"role": "test", "chat": "111", "content": "这是重roll后的新回复2", "name": "AI", "is_user": false, "send_date": "2026-03-12 18:26:50", "floor_number": 2, "swipes": ["这是重roll后的新回复", "这是重roll后的新回复2"], "swipes_id": 1}