249 lines
7.6 KiB
Python
249 lines
7.6 KiB
Python
"""
|
||
正则规则 API 路由
|
||
|
||
提供正则规则的 CRUD 操作和导入导出功能
|
||
"""
|
||
from fastapi import APIRouter, HTTPException, UploadFile, File
|
||
from pydantic import BaseModel
|
||
from typing import List, Optional
|
||
import json
|
||
import logging
|
||
|
||
from services.regex_service import regex_service
|
||
from models.regex_rules import RegexRule, RegexRuleset, RegexScope
|
||
from services.system_settings_service import system_settings_service
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
router = APIRouter(prefix="/api/regex", tags=["regex"])
|
||
|
||
|
||
# ==================== 数据模型 ====================
|
||
|
||
class RuleUpdateRequest(BaseModel):
|
||
"""规则更新请求"""
|
||
rule: RegexRule
|
||
scope: RegexScope
|
||
name: Optional[str] = None # 角色卡名称或预设名称(scope 为 CHARACTER/PRESET 时需要)
|
||
|
||
|
||
class SystemSettingsUpdate(BaseModel):
|
||
"""系统设置更新请求"""
|
||
thinkingTagPrefix: Optional[str] = None
|
||
thinkingTagSuffix: Optional[str] = None
|
||
currentPresetName: Optional[str] = None
|
||
|
||
|
||
# ==================== 规则查询 ====================
|
||
|
||
@router.get("/rules")
|
||
async def get_rules(
|
||
character_name: Optional[str] = None,
|
||
preset_name: Optional[str] = None
|
||
):
|
||
"""
|
||
获取适用的正则规则列表
|
||
|
||
Args:
|
||
character_name: 当前角色卡名称(可选)
|
||
preset_name: 当前预设名称(可选)
|
||
|
||
Returns:
|
||
规则列表
|
||
"""
|
||
try:
|
||
rules = regex_service.get_rules_for_context(character_name, preset_name)
|
||
return {
|
||
"success": True,
|
||
"rules": [rule.dict() for rule in rules],
|
||
"count": len(rules)
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"获取规则失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.get("/rulesets/global")
|
||
async def get_global_rulesets():
|
||
"""获取所有全局规则集"""
|
||
try:
|
||
rulesets = list(regex_service.global_rulesets.values())
|
||
return {
|
||
"success": True,
|
||
"rulesets": [rs.dict() for rs in rulesets]
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"获取全局规则集失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.get("/rulesets/character/{character_name}")
|
||
async def get_character_ruleset(character_name: str):
|
||
"""获取指定角色卡的规则集"""
|
||
try:
|
||
if character_name in regex_service.character_rulesets:
|
||
ruleset = regex_service.character_rulesets[character_name]
|
||
return {
|
||
"success": True,
|
||
"ruleset": ruleset.dict()
|
||
}
|
||
else:
|
||
return {
|
||
"success": True,
|
||
"ruleset": None
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"获取角色规则集失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.get("/rulesets/preset/{preset_name}")
|
||
async def get_preset_ruleset(preset_name: str):
|
||
"""获取指定预设的规则集"""
|
||
try:
|
||
if preset_name in regex_service.preset_rulesets:
|
||
ruleset = regex_service.preset_rulesets[preset_name]
|
||
return {
|
||
"success": True,
|
||
"ruleset": ruleset.dict()
|
||
}
|
||
else:
|
||
return {
|
||
"success": True,
|
||
"ruleset": None
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"获取预设规则集失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
# ==================== 规则管理 ====================
|
||
|
||
@router.post("/rules")
|
||
async def add_rule(request: RuleUpdateRequest):
|
||
"""添加新规则"""
|
||
try:
|
||
regex_service.save_ruleset(
|
||
RegexRuleset(
|
||
id=request.rule.id,
|
||
name=request.rule.scriptName,
|
||
rules=[request.rule]
|
||
),
|
||
request.scope,
|
||
request.name
|
||
)
|
||
|
||
# 重新加载规则
|
||
regex_service._load_all_rules()
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "规则添加成功"
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"添加规则失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.delete("/rules/{rule_id}")
|
||
async def delete_rule(rule_id: str):
|
||
"""删除规则(需要指定作用域和名称)"""
|
||
# TODO: 实现具体的删除逻辑
|
||
return {
|
||
"success": False,
|
||
"message": "暂未实现,请使用规则集级别的删除"
|
||
}
|
||
|
||
|
||
# ==================== 规则导入导出 ====================
|
||
|
||
@router.post("/import")
|
||
async def import_rules(file: UploadFile = File(...)):
|
||
"""
|
||
导入正则规则(支持 SillyTavern 格式)
|
||
|
||
可以导入:
|
||
1. 单个规则文件(JSON 数组)
|
||
2. 规则集文件(JSON 对象)
|
||
"""
|
||
try:
|
||
content = await file.read()
|
||
data = json.loads(content.decode('utf-8'))
|
||
|
||
# 判断格式并导入
|
||
if isinstance(data, list):
|
||
# SillyTavern 格式 - 导入为全局规则
|
||
ruleset = regex_service._convert_sillytavern_format(data, file.filename.replace('.json', ''))
|
||
regex_service.save_ruleset(ruleset, RegexScope.GLOBAL)
|
||
elif isinstance(data, dict):
|
||
if 'rules' in data:
|
||
# 规则集格式
|
||
ruleset = RegexRuleset(**data)
|
||
regex_service.save_ruleset(ruleset, RegexScope.GLOBAL)
|
||
else:
|
||
raise ValueError("未知的文件格式")
|
||
else:
|
||
raise ValueError("无效的文件格式")
|
||
|
||
# 重新加载规则
|
||
regex_service._load_all_rules()
|
||
|
||
return {
|
||
"success": True,
|
||
"message": f"成功导入规则集: {ruleset.name}"
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"导入规则失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.get("/export/global")
|
||
async def export_global_rules():
|
||
"""导出所有全局规则"""
|
||
try:
|
||
all_rulesets = list(regex_service.global_rulesets.values())
|
||
return {
|
||
"success": True,
|
||
"rulesets": [rs.dict() for rs in all_rulesets]
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"导出规则失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
# ==================== 系统设置 ====================
|
||
|
||
@router.get("/settings")
|
||
async def get_system_settings():
|
||
"""获取系统设置"""
|
||
try:
|
||
settings = system_settings_service.settings
|
||
return {
|
||
"success": True,
|
||
"settings": settings.dict()
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"获取系统设置失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.put("/settings")
|
||
async def update_system_settings(request: SystemSettingsUpdate):
|
||
"""更新系统设置"""
|
||
try:
|
||
if request.thinkingTagPrefix is not None or request.thinkingTagSuffix is not None:
|
||
prefix = request.thinkingTagPrefix or system_settings_service.settings.thinkingTagPrefix
|
||
suffix = request.thinkingTagSuffix or system_settings_service.settings.thinkingTagSuffix
|
||
system_settings_service.update_thinking_tags(prefix, suffix)
|
||
|
||
if request.currentPresetName is not None:
|
||
system_settings_service.update_current_preset(request.currentPresetName)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "系统设置已更新"
|
||
}
|
||
except Exception as e:
|
||
logger.error(f"更新系统设置失败: {e}")
|
||
raise HTTPException(status_code=500, detail=str(e))
|