初始化提交
This commit is contained in:
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
|
||||
Reference in new issue
Block a user