165 lines
5.8 KiB
Python
165 lines
5.8 KiB
Python
"""
|
||
正则替换规则模型
|
||
|
||
兼容 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: Optional[int] = Field(None, ge=0, description="最小消息深度(从最新消息开始计数,None 表示无限制)")
|
||
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))
|