""" 正则规则 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))