初始化提交

This commit is contained in:
chenxin committed 2026-08-21 16:00:13 +08:00
commit 887c1f449e
963 files changed
+166869

No files matched your search

@@ -0,0 +1,96 @@
"""默认人物模板加载器:从 ``character_templates.yaml`` 读取系统模板定义。
按项目约定,此文件放在应用服务层(``app/services/``),而非 ``core/config/``,
因为人物模板定义属于业务数据(出厂产品内容),而非部署环境配置。
"""
from __future__ import annotations
from typing import Any
import yaml
from pydantic import BaseModel, ConfigDict, Field
from app.core.config import settings
from app.core.errors import AppError
class CharacterTemplateConfig(BaseModel):
"""一条系统模板的配置定义。"""
model_config = ConfigDict(extra="forbid")
key: str = Field(description="模板唯一标识,对应 ORM 的 template_key。")
name: str = Field(description="模板人物内部名称。")
display_name: str | None = Field(default=None, description="模板人物显示昵称。")
language: str = Field(default="zh-CN", description="默认语言。")
privacy_level: str = Field(default="normal", description="隐私等级。")
is_default: bool = Field(
default=False,
description="注册新用户时是否以此模板创建默认角色。至多一个模板为 true,多个则取第一个。",
)
profile: dict[str, Any] = Field(
default_factory=dict,
description="人物档案,可含 background / speaking_style 等(不含 birthday——由创建时兜底)。",
)
model_policy: dict[str, Any] = Field(
default_factory=dict,
description="模型策略(不含 default_chat_model_hint——启动时从 settings 补充)。",
)
class CharacterTemplatesFile(BaseModel):
"""配置文件顶层结构。"""
model_config = ConfigDict(extra="forbid")
version: int = Field(description="配置文件版本号,当前仅支持 1。")
templates: list[CharacterTemplateConfig] = Field(description="系统模板定义列表。")
def load_character_templates() -> dict[str, CharacterTemplateConfig]:
"""读 ``character_templates.yaml`` → parse → validate → ``{key → config}`` 字典。
Returns:
空字典:文件不存在或内容为空时返回空。
Raises:
AppError: 文件存在但 YAML 解析失败或 version 不匹配。
"""
path = settings.resolved_character_templates_path
if not path.exists():
return {}
try:
raw = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
config = CharacterTemplatesFile.model_validate(raw)
except AppError:
raise
except Exception as exc:
raise AppError(
"config_error",
"人物模板配置文件解析失败。",
details={"path": str(path)},
) from exc
if config.version != 1:
raise AppError(
"config_error",
f"仅支持 character_templates.yaml version: 1,当前为 {config.version}。",
details={"path": str(path)},
)
return {t.key: t for t in config.templates}
def get_default_template() -> CharacterTemplateConfig:
"""便利函数:取 ``key='default'`` 的系统模板配置。
Raises:
AppError: 加载失败或缺少 default 模板。
"""
templates = load_character_templates()
template = templates.get("default")
if template is None:
raise AppError(
"config_error",
"人物模板配置中缺少 key='default' 的模板。",
)
return template