204 lines
6.2 KiB
Python
204 lines
6.2 KiB
Python
"""
|
||
Preset Service
|
||
预设服务层 - 处理预设的 CRUD 操作
|
||
"""
|
||
import json
|
||
import os
|
||
from pathlib import Path
|
||
from typing import List, Dict, Any, Optional
|
||
from datetime import datetime
|
||
|
||
from core.config import settings
|
||
|
||
|
||
class PresetService:
|
||
"""预设服务类"""
|
||
|
||
@staticmethod
|
||
def _extract_preset_name_from_filename(filename: str) -> str:
|
||
"""
|
||
从文件名提取预设名称,去掉时间戳和文件后缀
|
||
|
||
Args:
|
||
filename: 文件名(不含路径)
|
||
|
||
Returns:
|
||
清理后的预设名称
|
||
|
||
Examples:
|
||
"Default.json" -> "Default"
|
||
"MyPreset_1234567890.json" -> "MyPreset"
|
||
"Test_1714567890123.json" -> "Test"
|
||
"""
|
||
# 去掉 .json 后缀
|
||
name = filename.replace('.json', '')
|
||
|
||
# 去掉末尾的时间戳(下划线+数字组合)
|
||
# 匹配模式:_后面跟着10-13位数字(Unix时间戳)
|
||
import re
|
||
name = re.sub(r'_\d{10,13}$', '', name)
|
||
|
||
return name
|
||
|
||
@staticmethod
|
||
def _get_preset_path(name: str) -> Path:
|
||
"""获取预设文件路径"""
|
||
return settings.PRESET_PATH / f"{name}.json"
|
||
|
||
@staticmethod
|
||
def _load_preset(name: str) -> Optional[Dict[str, Any]]:
|
||
"""加载预设 JSON 文件"""
|
||
path = PresetService._get_preset_path(name)
|
||
if not path.exists():
|
||
return None
|
||
|
||
try:
|
||
with open(path, 'r', encoding='utf-8') as f:
|
||
return json.load(f)
|
||
except Exception as e:
|
||
raise ValueError(f"Failed to load preset '{name}': {str(e)}")
|
||
|
||
@staticmethod
|
||
def _save_preset(name: str, data: Dict[str, Any]):
|
||
"""保存预设到 JSON 文件"""
|
||
path = PresetService._get_preset_path(name)
|
||
try:
|
||
with open(path, 'w', encoding='utf-8') as f:
|
||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||
except Exception as e:
|
||
raise ValueError(f"Failed to save preset '{name}': {str(e)}")
|
||
|
||
@staticmethod
|
||
def list_presets() -> List[Dict[str, Any]]:
|
||
"""
|
||
获取所有预设的列表(仅基本信息)
|
||
|
||
Returns:
|
||
预设列表,每个包含 name, description, component_count, temperature 等
|
||
"""
|
||
presets = []
|
||
|
||
for json_file in settings.PRESET_PATH.glob("*.json"):
|
||
try:
|
||
with open(json_file, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
|
||
# 计算组件数量
|
||
entries = data.get("entries", [])
|
||
prompts = data.get("prompts", [])
|
||
component_count = len(entries) if entries else len(prompts)
|
||
|
||
# 提取温度参数(支持内部结构和 SillyTavern 结构)
|
||
temperature = data.get("temperature", 1.0)
|
||
|
||
# 从文件名提取预设名称(去掉时间戳和后缀)
|
||
preset_name = PresetService._extract_preset_name_from_filename(json_file.name)
|
||
|
||
preset_info = {
|
||
"name": preset_name,
|
||
"description": data.get("description", ""),
|
||
"component_count": component_count,
|
||
"temperature": temperature
|
||
}
|
||
|
||
presets.append(preset_info)
|
||
except Exception as e:
|
||
print(f"Error loading preset {json_file.name}: {e}")
|
||
continue
|
||
|
||
# 按名称排序
|
||
presets.sort(key=lambda x: x.get("name", ""))
|
||
return presets
|
||
|
||
@staticmethod
|
||
def get_preset(name: str) -> Dict[str, Any]:
|
||
"""
|
||
获取指定预设的完整数据
|
||
|
||
Args:
|
||
name: 预设名称
|
||
|
||
Returns:
|
||
预设完整数据
|
||
"""
|
||
data = PresetService._load_preset(name)
|
||
if not data:
|
||
raise FileNotFoundError(f"Preset '{name}' not found")
|
||
|
||
return data
|
||
|
||
@staticmethod
|
||
def create_preset(name: str, preset_data: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
创建新预设
|
||
|
||
Args:
|
||
name: 预设名称
|
||
preset_data: 预设数据
|
||
|
||
Returns:
|
||
创建的预设数据
|
||
"""
|
||
# 检查是否已存在
|
||
if PresetService._get_preset_path(name).exists():
|
||
raise ValueError(f"Preset '{name}' already exists")
|
||
|
||
# 确保有必要的字段
|
||
if "name" not in preset_data:
|
||
preset_data["name"] = name
|
||
|
||
# 添加时间戳
|
||
now = int(datetime.now().timestamp())
|
||
if "createdAt" not in preset_data:
|
||
preset_data["createdAt"] = now
|
||
if "updatedAt" not in preset_data:
|
||
preset_data["updatedAt"] = now
|
||
|
||
PresetService._save_preset(name, preset_data)
|
||
return preset_data
|
||
|
||
@staticmethod
|
||
def update_preset(name: str, update_data: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
更新预设
|
||
|
||
Args:
|
||
name: 预设名称
|
||
update_data: 要更新的数据
|
||
|
||
Returns:
|
||
更新后的预设数据
|
||
"""
|
||
data = PresetService._load_preset(name)
|
||
if not data:
|
||
raise FileNotFoundError(f"Preset '{name}' not found")
|
||
|
||
# 更新字段
|
||
for key, value in update_data.items():
|
||
if key not in ["name", "createdAt"]: # 不允许修改名称和创建时间
|
||
data[key] = value
|
||
|
||
# 更新时间戳
|
||
data["updatedAt"] = int(datetime.now().timestamp())
|
||
|
||
PresetService._save_preset(name, data)
|
||
return data
|
||
|
||
@staticmethod
|
||
def delete_preset(name: str) -> bool:
|
||
"""
|
||
删除预设
|
||
|
||
Args:
|
||
name: 预设名称
|
||
|
||
Returns:
|
||
是否删除成功
|
||
"""
|
||
path = PresetService._get_preset_path(name)
|
||
if not path.exists():
|
||
raise FileNotFoundError(f"Preset '{name}' not found")
|
||
|
||
path.unlink()
|
||
return True
|