完成请求推送,但组装mes还有问题
This commit is contained in:
@@ -141,7 +141,14 @@ class CharacterCard(BaseModel):
|
||||
outputSchema: Optional[List[OutputSchemaField]] = Field(None, description="输出 schema 定义 (结构化输出)")
|
||||
avatarPath: Optional[str] = Field(None, description="角色头像路径")
|
||||
alternate_greetings: Optional[List[str]] = Field(None, description="替代问候语数组")
|
||||
tags: Optional[List[str]] = Field(None, description="标签数组")
|
||||
|
||||
# TODO: 拓展提示词设置(插件/拓展系统预留接口)
|
||||
# - tableMaintenancePrompt: 用于指导 AI 维护动态表格(RPG状态、任务追踪等)
|
||||
# - imageGenerationPrompt: 用于指导 AI 生成图片描述提示词
|
||||
# 当前状态:字段已定义,默认值为 None,等待插件系统实现
|
||||
tableMaintenancePrompt: Optional[str] = Field(None, description="动态表格维护提示词 - 指导 AI 如何更新表格数据")
|
||||
imageGenerationPrompt: Optional[str] = Field(None, description="生图提示词模板 - 指导 AI 如何生成图片描述")
|
||||
|
||||
createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
|
||||
updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
|
||||
lastChatAt: Optional[int] = Field(None, description="最后聊天时间戳")
|
||||
@@ -300,3 +307,84 @@ class ChatRAGConfig(BaseModel):
|
||||
indexConfig: Optional[Dict[str, Any]] = Field(None, description="索引配置")
|
||||
createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
|
||||
updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
|
||||
|
||||
|
||||
# ==================== Token 统计 ====================
|
||||
|
||||
class TokenUsageStatus(str, Enum):
|
||||
"""Token 使用状态"""
|
||||
COMPLETED = 'completed' # 成功完成
|
||||
INTERRUPTED = 'interrupted' # 被用户中断
|
||||
FAILED = 'failed' # 请求失败(API错误等)
|
||||
|
||||
|
||||
class TokenUsageRecord(BaseModel):
|
||||
"""
|
||||
Token 使用记录
|
||||
|
||||
记录每次 LLM 调用的 token 使用情况,支持按时间、角色、聊天维度统计
|
||||
"""
|
||||
id: str = Field(..., description="记录唯一标识符 (UUID)")
|
||||
chatId: str = Field(..., description="聊天ID (role_name/chat_name)")
|
||||
roleName: str = Field(..., description="角色名称")
|
||||
chatName: str = Field(..., description="聊天名称")
|
||||
messageId: Optional[str] = Field(None, description="关联的消息ID")
|
||||
floor: Optional[int] = Field(None, description="楼层号")
|
||||
|
||||
# Token 统计
|
||||
promptTokens: int = Field(0, description="输入 token 数")
|
||||
completionTokens: int = Field(0, description="输出 token 数")
|
||||
totalTokens: int = Field(0, description="总 token 数")
|
||||
|
||||
# 状态信息
|
||||
status: TokenUsageStatus = Field(TokenUsageStatus.COMPLETED, description="请求状态")
|
||||
errorMessage: Optional[str] = Field(None, description="错误信息(如果失败)")
|
||||
|
||||
# 时间信息
|
||||
timestamp: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="请求时间戳")
|
||||
duration: Optional[float] = Field(None, description="请求耗时(秒)")
|
||||
|
||||
# API 信息
|
||||
model: Optional[str] = Field(None, description="使用的模型")
|
||||
apiProvider: Optional[str] = Field(None, description="API 提供商")
|
||||
apiUrl: Optional[str] = Field(None, description="API URL地址")
|
||||
|
||||
|
||||
# ==================== 图片元数据 ====================
|
||||
|
||||
class ImageMetadata(BaseModel):
|
||||
"""
|
||||
图片元数据
|
||||
|
||||
记录生成的图片信息,绑定到角色/聊天的特定楼层
|
||||
"""
|
||||
id: str = Field(..., description="图片唯一标识符 (UUID)")
|
||||
chatId: str = Field(..., description="聊天ID (role_name/chat_name)")
|
||||
roleName: str = Field(..., description="角色名称")
|
||||
chatName: str = Field(..., description="聊天名称")
|
||||
floor: int = Field(..., description="楼层号")
|
||||
|
||||
# 图片信息
|
||||
filename: str = Field(..., description="文件名")
|
||||
filepath: str = Field(..., description="文件相对路径")
|
||||
width: Optional[int] = Field(None, description="图片宽度")
|
||||
height: Optional[int] = Field(None, description="图片高度")
|
||||
fileSize: Optional[int] = Field(None, description="文件大小(字节)")
|
||||
|
||||
# Swipe 支持
|
||||
swipeIndex: int = Field(0, description="Swipe 索引(同一楼层多张图片)")
|
||||
isCurrentSwipe: bool = Field(True, description="是否为当前显示的 swipe")
|
||||
|
||||
# 生成信息
|
||||
prompt: Optional[str] = Field(None, description="生成使用的提示词")
|
||||
negativePrompt: Optional[str] = Field(None, description="负面提示词")
|
||||
seed: Optional[int] = Field(None, description="随机种子")
|
||||
model: Optional[str] = Field(None, description="使用的模型/checkpoint")
|
||||
workflowName: Optional[str] = Field(None, description="使用的工作流名称")
|
||||
|
||||
# 任务信息
|
||||
taskId: Optional[str] = Field(None, description="关联的任务ID")
|
||||
generationTime: Optional[float] = Field(None, description="生成耗时(秒)")
|
||||
|
||||
# 时间信息
|
||||
createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
|
||||
|
||||
164
backend/models/regex_rules.py
Normal file
164
backend/models/regex_rules.py
Normal file
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
正则替换规则模型
|
||||
|
||||
兼容 SillyTavern 的正则系统,支持全局、角色卡、预设三种作用域。
|
||||
"""
|
||||
from enum import Enum
|
||||
from typing import List, Optional, Dict, Any
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class RegexPlacement(int, Enum):
|
||||
"""
|
||||
正则应用位置(对应 SillyTavern 的 placement 数组)
|
||||
|
||||
0: System Prompt - 系统提示词
|
||||
1: User Input - 用户输入
|
||||
2: AI Output - AI 输出
|
||||
3: Quick Reply - 快捷回复
|
||||
4: World Info - 世界书信息
|
||||
5: Reasoning/Thinking - 推理/思考内容
|
||||
"""
|
||||
SYSTEM_PROMPT = 0
|
||||
USER_INPUT = 1
|
||||
AI_OUTPUT = 2
|
||||
QUICK_REPLY = 3
|
||||
WORLD_INFO = 4
|
||||
REASONING = 5
|
||||
|
||||
|
||||
class RegexScope(str, Enum):
|
||||
"""
|
||||
正则规则作用域
|
||||
|
||||
- GLOBAL: 全局生效,对所有聊天应用
|
||||
- CHARACTER: 绑定到特定角色卡
|
||||
- PRESET: 绑定到特定预设
|
||||
"""
|
||||
GLOBAL = 'global'
|
||||
CHARACTER = 'character'
|
||||
PRESET = 'preset'
|
||||
|
||||
|
||||
class SubstituteMode(int, Enum):
|
||||
"""
|
||||
替换模式
|
||||
|
||||
对应 SillyTavern 的 substituteRegex 字段
|
||||
"""
|
||||
REPLACE_ALL = 0 # 替换所有匹配
|
||||
REPLACE_FIRST = 1 # 仅替换首次匹配
|
||||
REPLACE_CAPTURED = 2 # 替换捕获组
|
||||
|
||||
|
||||
class RegexRule(BaseModel):
|
||||
"""
|
||||
单条正则替换规则
|
||||
|
||||
完全兼容 SillyTavern 的正则规则格式
|
||||
"""
|
||||
id: str = Field(..., description="规则唯一标识符 (UUID)")
|
||||
scriptName: str = Field(..., description="脚本名称(用于显示)")
|
||||
|
||||
# 核心正则配置
|
||||
findRegex: str = Field(..., description="查找正则表达式(如:/<thinking>[\\s\\S]*?<\\/thinking>/gi)")
|
||||
replaceString: str = Field("", description="替换字符串(支持捕获组引用 $1, $2 等)")
|
||||
trimStrings: List[str] = Field(default_factory=list, description="要额外修剪的字符串数组")
|
||||
|
||||
# 应用位置(关键!对应 SillyTavern 的 placement 数组)
|
||||
placement: List[RegexPlacement] = Field(
|
||||
default_factory=lambda: [RegexPlacement.AI_OUTPUT],
|
||||
description="应用位置数组:0=系统提示词, 1=用户输入, 2=AI输出, 3=快捷回复, 4=世界书, 5=推理内容"
|
||||
)
|
||||
|
||||
# 替换模式
|
||||
substituteRegex: SubstituteMode = Field(
|
||||
SubstituteMode.REPLACE_ALL,
|
||||
description="替换模式:0=全部,1=首次,2=捕获组"
|
||||
)
|
||||
|
||||
# 作用范围控制
|
||||
markdownOnly: bool = Field(False, description="是否仅应用于 Markdown 渲染后的内容")
|
||||
promptOnly: bool = Field(False, description="是否仅应用于发送给 LLM 的提示词")
|
||||
runOnEdit: bool = Field(True, description="用户编辑消息时是否重新应用")
|
||||
|
||||
# 消息深度控制
|
||||
minDepth: int = Field(0, ge=0, description="最小消息深度(从最新消息开始计数)")
|
||||
maxDepth: Optional[int] = Field(None, ge=0, description="最大消息深度(None 表示无限制)")
|
||||
|
||||
# 作用域配置
|
||||
scope: RegexScope = Field(RegexScope.GLOBAL, description="规则作用域")
|
||||
characterName: Optional[str] = Field(None, description="绑定的角色卡名称(scope=CHARACTER 时使用)")
|
||||
presetName: Optional[str] = Field(None, description="绑定的预设名称(scope=PRESET 时使用)")
|
||||
|
||||
# 启用状态
|
||||
disabled: bool = Field(False, description="是否禁用此规则(与 enabled 相反,为了兼容 ST)")
|
||||
|
||||
# 执行顺序
|
||||
order: int = Field(0, description="执行顺序(数值越小越先执行)")
|
||||
|
||||
# 元数据
|
||||
createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
|
||||
updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
|
||||
description: Optional[str] = Field(None, description="规则描述(可选)")
|
||||
|
||||
|
||||
class RegexRuleset(BaseModel):
|
||||
"""
|
||||
正则规则集
|
||||
|
||||
一组正则规则的集合,可以整体导入/导出,兼容 SillyTavern 格式
|
||||
"""
|
||||
id: str = Field(..., description="规则集唯一标识符 (UUID)")
|
||||
name: str = Field(..., description="规则集名称")
|
||||
description: Optional[str] = Field(None, description="规则集描述")
|
||||
|
||||
rules: List[RegexRule] = Field(default_factory=list, description="规则列表")
|
||||
|
||||
# 元数据
|
||||
createdAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="创建时间戳")
|
||||
updatedAt: int = Field(default_factory=lambda: int(datetime.now().timestamp()), description="最后更新时间戳")
|
||||
version: int = Field(1, description="版本号(用于数据迁移)")
|
||||
|
||||
# SillyTavern 兼容性标记
|
||||
isSillyTavernFormat: bool = Field(False, description="是否为 SillyTavern 导入格式")
|
||||
|
||||
|
||||
# ==================== 使用示例 ====================
|
||||
|
||||
if __name__ == '__main__':
|
||||
import json
|
||||
|
||||
# 创建一条规则
|
||||
rule = RegexRule(
|
||||
id="example-hide-thinking-001",
|
||||
scriptName="隐藏思考标签",
|
||||
findRegex=r"<thinking>[\s\S]*?<\/thinking>",
|
||||
replaceString="",
|
||||
trimStrings=[],
|
||||
placement=[RegexPlacement.AI_OUTPUT],
|
||||
substituteRegex=SubstituteMode.REPLACE_ALL,
|
||||
markdownOnly=False,
|
||||
promptOnly=False,
|
||||
runOnEdit=True,
|
||||
minDepth=0,
|
||||
maxDepth=None,
|
||||
scope=RegexScope.GLOBAL,
|
||||
characterName=None,
|
||||
presetName=None,
|
||||
disabled=False,
|
||||
order=1,
|
||||
description="隐藏 AI 回复中的 <thinking> 标签及其内容"
|
||||
)
|
||||
|
||||
# 创建规则集
|
||||
ruleset = RegexRuleset(
|
||||
id="ruleset-001",
|
||||
name="默认正则规则集",
|
||||
description="包含常用的文本处理规则",
|
||||
rules=[rule]
|
||||
)
|
||||
|
||||
# 导出为 JSON(兼容 SillyTavern)
|
||||
print(json.dumps(ruleset.dict(), indent=2, ensure_ascii=False))
|
||||
49
backend/models/system_settings.py
Normal file
49
backend/models/system_settings.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
系统设置模型
|
||||
|
||||
包含全局配置,如思考标签前后缀等。
|
||||
"""
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class SystemSettings(BaseModel):
|
||||
"""
|
||||
系统全局设置
|
||||
|
||||
持久化存储到 data/system_settings.json
|
||||
"""
|
||||
|
||||
# ==================== 思考标签配置 ====================
|
||||
thinkingTagPrefix: str = Field(
|
||||
"<thinking>",
|
||||
description="思考标签前缀(默认:<thinking>)"
|
||||
)
|
||||
thinkingTagSuffix: str = Field(
|
||||
"</thinking>",
|
||||
description="思考标签后缀(默认:</thinking>)"
|
||||
)
|
||||
|
||||
# ==================== 当前选中的预设 ====================
|
||||
currentPresetName: Optional[str] = Field(
|
||||
None,
|
||||
description="当前选中的预设名称(用于确定全局正则的作用域)"
|
||||
)
|
||||
|
||||
# ==================== 元数据 ====================
|
||||
updatedAt: int = Field(
|
||||
default_factory=lambda: int(datetime.now().timestamp()),
|
||||
description="最后更新时间戳"
|
||||
)
|
||||
version: int = Field(1, description="版本号")
|
||||
|
||||
|
||||
# ==================== 默认设置 ====================
|
||||
|
||||
DEFAULT_SYSTEM_SETTINGS = SystemSettings()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import json
|
||||
print(json.dumps(DEFAULT_SYSTEM_SETTINGS.dict(), indent=2, ensure_ascii=False))
|
||||
Reference in New Issue
Block a user