初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1 @@
|
||||
"""Pydantic schema 包。"""
|
||||
@@ -0,0 +1,323 @@
|
||||
"""司命台管理 API schema(api-spec §19)。
|
||||
|
||||
九枢健康度聚合 / 朱批待办 / 起居注审计 + 五组治理对象 CRUD。
|
||||
枚举取值见契约字典 §5.5 / §5.7 / §5.9 / §5.10 / §5.11–§5.14 / §11 / §13。
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
# —— §19.1 九枢健康度聚合 ——
|
||||
|
||||
|
||||
class PivotHealthSummary(BaseModel):
|
||||
pivot: str = Field(description="九枢标识(契约字典 §5.9)。")
|
||||
name: str = Field(description="枢中文名。")
|
||||
module: str = Field(description="工程模块名。")
|
||||
health: str = Field(description="健康状态:void / breathing / steady / cracked(§5.10)。")
|
||||
summary: str = Field(description="人类可读的一句话态势;cracked 须给出原因。")
|
||||
updated_at: str | None = Field(default=None, description="最近更新时间。")
|
||||
|
||||
|
||||
class PivotHealthReport(BaseModel):
|
||||
overall: str = Field(description="九枢聚合健康度;存在任一 cracked 则为 cracked。")
|
||||
alerts: int = Field(description="cracked 枢计数。")
|
||||
pivots: list[PivotHealthSummary] = Field(
|
||||
description="九枢全集,缺数据枢 health=void,不得省略。"
|
||||
)
|
||||
|
||||
|
||||
# —— §19.2 朱批待办 ——
|
||||
|
||||
|
||||
class PendingApproval(BaseModel):
|
||||
confirmation_id: str = Field(
|
||||
description="确认 ID,用于 /safety/confirmations/{id}/approve|deny。"
|
||||
)
|
||||
character_id: str | None = Field(default=None, description="所属人物 ID。")
|
||||
tool: str = Field(description="待裁定工具名。")
|
||||
risk_level: str = Field(description="风险等级(§5.5)。")
|
||||
permission_policy: str = Field(description="权限策略(§5.6)。")
|
||||
action_summary: str = Field(description="动作摘要。")
|
||||
input_summary: str | None = Field(default=None, description="输入摘要,不得含明文密钥。")
|
||||
possible_impact: str | None = Field(default=None, description="可能影响。")
|
||||
reason: str | None = Field(default=None, description="裁定原因。")
|
||||
created_at: str | None = Field(default=None, description="创建时间。")
|
||||
|
||||
|
||||
# —— §19.3 起居注审计 ——
|
||||
|
||||
|
||||
class AuditEntry(BaseModel):
|
||||
audit_id: str = Field(description="审计事件 ID。")
|
||||
pivot: str = Field(description="九枢标识。")
|
||||
actor: str = Field(description="发起者:user / system / tool(§13.2)。")
|
||||
action: str = Field(description="动作。")
|
||||
subject: str | None = Field(default=None, description="动作对象。")
|
||||
decision: str = Field(description="裁定结果:auto / approved / denied / blocked(§13.3)。")
|
||||
risk_level: str | None = Field(default=None, description="风险等级。")
|
||||
summary: str = Field(description="人类可读一句话摘要。")
|
||||
trace_id: str | None = Field(default=None, description="链路追踪 ID。")
|
||||
created_at: str | None = Field(default=None, description="创建时间。")
|
||||
|
||||
|
||||
# —— §19.4 太一枢 · 目标 ——
|
||||
|
||||
|
||||
class GoalNode(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
character_id: str | None = None
|
||||
title: str
|
||||
description: str
|
||||
level: str = Field(description="目标层级(§5.11)。")
|
||||
type: str = Field(
|
||||
default="session",
|
||||
description="目标类型(§5.15):mission/relationship/skill/knowledge/maintenance/exploration",
|
||||
)
|
||||
status: str = Field(description="目标状态(§5.12)。")
|
||||
priority: int
|
||||
progress: float = Field(default=0.0, ge=0.0, le=1.0, description="完成度 0~1。")
|
||||
constraints: list[str]
|
||||
parent_id: str | None = None
|
||||
success_criteria: list[str]
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class CreateGoalRequest(BaseModel):
|
||||
title: str = Field(max_length=300)
|
||||
description: str = Field(default="", max_length=2000)
|
||||
level: str = Field(description="目标层级(§5.11)。")
|
||||
type: str = Field(default="session", description="目标类型(§5.15)。")
|
||||
priority: int = Field(default=50, ge=0, le=100)
|
||||
character_id: str | None = Field(default=None, description="归属于哪个人物。")
|
||||
constraints: list[str] = Field(default_factory=list)
|
||||
parent_id: str | None = None
|
||||
success_criteria: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UpdateGoalRequest(BaseModel):
|
||||
title: str | None = Field(default=None, max_length=300)
|
||||
description: str | None = Field(default=None, max_length=2000)
|
||||
status: str | None = Field(default=None, description="目标状态(§5.12)。")
|
||||
type: str | None = Field(default=None, description="目标类型(§5.15)。")
|
||||
priority: int | None = Field(default=None, ge=0, le=100)
|
||||
progress: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
constraints: list[str] | None = None
|
||||
|
||||
|
||||
# —— §19.4 烛照枢 · 感知通道 ——
|
||||
|
||||
|
||||
class PerceptionChannelResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
channel: str
|
||||
name: str
|
||||
description: str
|
||||
enabled: bool
|
||||
consent_status: str = Field(description="授权状态(§5.7)。")
|
||||
privacy_level: str = Field(description="隐私等级(§5.4)。")
|
||||
risk_level: str = Field(description="风险等级(§5.5)。")
|
||||
last_active_at: str | None = None
|
||||
|
||||
|
||||
class CreatePerceptionChannelRequest(BaseModel):
|
||||
channel: str = Field(max_length=80, description="通道标识;默认六类,可自定义。")
|
||||
name: str = Field(max_length=120)
|
||||
description: str = Field(default="", max_length=2000)
|
||||
risk_level: str = Field(default="L2")
|
||||
|
||||
|
||||
class UpdatePerceptionChannelRequest(BaseModel):
|
||||
enabled: bool | None = None
|
||||
consent_status: str | None = Field(default=None, description="授权状态(§5.7)。")
|
||||
privacy_level: str | None = Field(default=None, description="隐私等级(§5.4)。")
|
||||
|
||||
|
||||
# —— §19.4 河图枢 · 世界实体 ——
|
||||
|
||||
|
||||
class WorldEntityResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
type: str = Field(description="实体类型(§11.1)。")
|
||||
name: str
|
||||
attributes: dict[str, str]
|
||||
state_type: str = Field(description="状态类型(§11.2)。")
|
||||
confidence: float
|
||||
sources: list[str]
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class CreateWorldEntityRequest(BaseModel):
|
||||
type: str = Field(description="实体类型(§11.1)。")
|
||||
name: str = Field(max_length=200)
|
||||
attributes: dict[str, str] = Field(default_factory=dict)
|
||||
state_type: str = Field(default="inference", description="状态类型(§11.2)。")
|
||||
confidence: float = Field(default=0.5, ge=0.0, le=1.0)
|
||||
sources: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UpdateWorldEntityRequest(BaseModel):
|
||||
name: str | None = Field(default=None, max_length=200)
|
||||
state_type: str | None = Field(default=None, description="状态类型(§11.2)。")
|
||||
confidence: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
|
||||
|
||||
# —— §19.4 洛书枢 · 计划 ——
|
||||
|
||||
|
||||
class PlanSummaryResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
goal_id: str
|
||||
title: str
|
||||
status: str = Field(description="计划状态(§5.13)。")
|
||||
assumptions: list[str]
|
||||
risks: list[str]
|
||||
required_tools: list[str]
|
||||
success_criteria: list[str]
|
||||
step_count: int
|
||||
completed_steps: int
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class CreatePlanRequest(BaseModel):
|
||||
title: str = Field(max_length=300)
|
||||
goal_id: str | None = None
|
||||
assumptions: list[str] = Field(default_factory=list)
|
||||
risks: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UpdatePlanRequest(BaseModel):
|
||||
status: str | None = Field(default=None, description="计划状态(§5.13)。")
|
||||
assumptions: list[str] | None = None
|
||||
risks: list[str] | None = None
|
||||
|
||||
|
||||
# —— §19.4 天工阁枢 · 角色在岗 ——
|
||||
|
||||
|
||||
class AgentRosterResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
role: str
|
||||
name: str
|
||||
responsibility: str
|
||||
enabled: bool
|
||||
presence: str = Field(description="在岗呈现态(派生自最近活跃时间):idle / active。")
|
||||
last_active_at: str | None = None
|
||||
|
||||
|
||||
class CreateAgentRequest(BaseModel):
|
||||
role: str = Field(max_length=80, description="角色标识;默认九角色,可自定义。")
|
||||
name: str = Field(max_length=120)
|
||||
responsibility: str = Field(default="", max_length=2000)
|
||||
|
||||
|
||||
class UpdateAgentRequest(BaseModel):
|
||||
enabled: bool | None = None
|
||||
|
||||
|
||||
# —— §19.5 链路追踪 ——
|
||||
|
||||
|
||||
class TraceItem(BaseModel):
|
||||
kind: Literal["event", "audit", "idempotency"] = Field(
|
||||
description="记录来源:运行事件 / 安全审计 / 请求幂等。"
|
||||
)
|
||||
id: str = Field(description="记录主键。")
|
||||
created_at: datetime = Field(description="发生时间(前端按此排序展示)。")
|
||||
source_label: str = Field(description="人类可读来源(如「运行事件 / message.completed」)。")
|
||||
summary: str = Field(description="一句话摘要。")
|
||||
detail: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="原始字段(payload / decision / path 等),供详情展开。",
|
||||
)
|
||||
|
||||
|
||||
class TraceDetail(BaseModel):
|
||||
trace_id: str = Field(description="被查询的链路追踪 ID。")
|
||||
character_ids: list[str] = Field(
|
||||
default_factory=list, description="该链路涉及的人物去重列表(由 events / audits 聚合)。"
|
||||
)
|
||||
run_ids: list[str] = Field(
|
||||
default_factory=list, description="该链路涉及的 run 去重列表(由 events 聚合)。"
|
||||
)
|
||||
session_ids: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="该链路涉及的对话(chat session)去重列表(由 runs 聚合)。",
|
||||
)
|
||||
items: list[TraceItem] = Field(
|
||||
default_factory=list, description="按 created_at 升序的时间线条目。"
|
||||
)
|
||||
|
||||
|
||||
class TraceSummary(BaseModel):
|
||||
trace_id: str = Field(description="链路追踪 ID。")
|
||||
last_seen: datetime = Field(description="该链路最近一次活动时间。")
|
||||
character_ids: list[str] = Field(default_factory=list, description="涉及的人物去重列表。")
|
||||
session_ids: list[str] = Field(default_factory=list, description="涉及的对话去重列表。")
|
||||
event_count: int = Field(default=0, description="运行事件数。")
|
||||
audit_count: int = Field(default=0, description="安全审计数。")
|
||||
idempotency_count: int = Field(default=0, description="请求幂等数。")
|
||||
|
||||
|
||||
class TraceListResponse(BaseModel):
|
||||
items: list[TraceSummary] = Field(description="按 last_seen 倒序的链路概览。")
|
||||
total: int = Field(description="distinct trace 总数(未截断)。")
|
||||
|
||||
|
||||
# —— §8 司命台 · 起居注时间河(辰正 time.* 事件)——
|
||||
|
||||
|
||||
class TimeEventResponse(BaseModel):
|
||||
"""辰正 time.* 事件(起居注时间河条目)。"""
|
||||
|
||||
id: str = Field(description="事件 ID。")
|
||||
type: str = Field(description="事件类型(time.* 前缀,如 time.reunion / time.sentiment)。")
|
||||
payload: dict[str, Any] = Field(default_factory=dict, description="事件 payload(已解析)。")
|
||||
run_id: str | None = Field(default=None, description="所属 run ID(可空)。")
|
||||
character_id: str | None = Field(default=None, description="所属人物 ID(可空)。")
|
||||
sensitivity: str = Field(default="low", description="敏感度(§5.4)。")
|
||||
created_at: str = Field(description="发生时间(ISO 字符串)。")
|
||||
|
||||
|
||||
class TimeEventListResponse(BaseModel):
|
||||
items: list[TimeEventResponse] = Field(description="按 created_at 倒序的事件列表。")
|
||||
total: int = Field(description="source='time_sense' 的事件总数(未截断)。")
|
||||
|
||||
|
||||
# —— §19.6 用户管理 ——
|
||||
|
||||
|
||||
class AdminUserResponse(BaseModel):
|
||||
"""管理员视角的用户信息。"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
username: str
|
||||
email: str
|
||||
is_active: bool
|
||||
is_admin: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime | None = None
|
||||
|
||||
|
||||
class AdminCreateUserRequest(BaseModel):
|
||||
username: str = Field(min_length=1, description="用户名。")
|
||||
email: str = Field(min_length=1, description="邮箱。")
|
||||
password: str = Field(min_length=6, description="密码(>=6 字符)。")
|
||||
is_admin: bool = Field(default=False, description="是否管理员。")
|
||||
|
||||
|
||||
class AdminUpdateUserRequest(BaseModel):
|
||||
is_active: bool | None = Field(default=None, description="是否启用。")
|
||||
is_admin: bool | None = Field(default=None, description="是否管理员。")
|
||||
@@ -0,0 +1,70 @@
|
||||
"""司命台 · 人物模板管理 API schema。
|
||||
|
||||
对应 admin 端系统模板 CRUD(api-spec §19 扩展)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class AdminTemplateSummaryResponse(BaseModel):
|
||||
"""系统模板列表项(管理端完整视图)。"""
|
||||
|
||||
key: str = Field(description="模板唯一标识(template_key)。")
|
||||
name: str = Field(description="模板人物内部名称。")
|
||||
display_name: str | None = Field(description="模板人物显示昵称。")
|
||||
language: str = Field(description="默认语言。")
|
||||
privacy_level: str = Field(description="隐私等级。")
|
||||
is_default: bool = Field(
|
||||
default=False, description="是否默认模板(新用户以此创建初始角色)。"
|
||||
)
|
||||
profile: dict[str, Any] = Field(description="完整人物档案。")
|
||||
model_policy: dict[str, Any] = Field(description="完整模型策略。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601。")
|
||||
|
||||
|
||||
class AdminCreateTemplateRequest(BaseModel):
|
||||
"""创建系统模板。"""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
key: str = Field(
|
||||
min_length=1,
|
||||
pattern=r"^[a-z][a-z0-9_\-]*$",
|
||||
description="模板唯一标识,仅允许小写字母、数字、下划线、连字符。"
|
||||
"如与 YAML 中已有的 key 相同,下次重启时会被 YAML 配置覆盖。",
|
||||
)
|
||||
name: str = Field(min_length=1, 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="人物档案。生日 (birthday) 无需提供,系统自动兜底。",
|
||||
)
|
||||
model_policy: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="模型策略。default_chat_model_hint 无需提供,从 settings 自动补充。",
|
||||
)
|
||||
|
||||
|
||||
class AdminUpdateTemplateRequest(BaseModel):
|
||||
"""更新系统模板(所有字段可选,仅更新提供值的字段)。"""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
name: str | None = Field(default=None, description="内部名称。")
|
||||
display_name: str | None = Field(default=None, description="显示昵称。")
|
||||
language: str | None = Field(default=None, description="默认语言。")
|
||||
privacy_level: str | None = Field(default=None, description="隐私等级。")
|
||||
is_default: bool | None = Field(default=None, description="是否默认模板。")
|
||||
profile: dict[str, Any] | None = Field(default=None, description="人物档案。")
|
||||
model_policy: dict[str, Any] | None = Field(default=None, description="模型策略。")
|
||||
@@ -0,0 +1,63 @@
|
||||
"""认证 API schema。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
username: str = ... # min_length=1 由 pydantic v2 隐式保证
|
||||
email: str = ...
|
||||
password: str = ...
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
username: str = ...
|
||||
password: str = ...
|
||||
|
||||
|
||||
class RefreshRequest(BaseModel):
|
||||
refresh_token: str = ...
|
||||
|
||||
|
||||
class UserResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
username: str
|
||||
email: str
|
||||
is_admin: bool
|
||||
permissions: list[str] = []
|
||||
created_at: str
|
||||
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
expires_in: int
|
||||
user: UserResponse
|
||||
|
||||
|
||||
class CurrentUser(BaseModel):
|
||||
"""从 get_current_user 依赖注入的当前用户信息。"""
|
||||
|
||||
user_id: str
|
||||
username: str
|
||||
email: str
|
||||
owner_scope: str # 当前等于 user_id
|
||||
is_admin: bool
|
||||
permissions: list[str] = []
|
||||
|
||||
|
||||
class UpdateProfileRequest(BaseModel):
|
||||
"""更新个人资料请求。"""
|
||||
|
||||
email: str | None = None
|
||||
|
||||
|
||||
class ChangePasswordRequest(BaseModel):
|
||||
"""修改密码请求。"""
|
||||
|
||||
old_password: str
|
||||
new_password: str
|
||||
@@ -0,0 +1,50 @@
|
||||
"""候选 API schema(api-spec §8)。"""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ProfileCandidateResponse(BaseModel):
|
||||
id: str = Field(description="候选 ID,前缀 profile_candidate_。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
import_job_id: str | None = Field(default=None, description="来源导入任务 ID。")
|
||||
field: str = Field(description="档案字段名。")
|
||||
value: str = Field(description="候选值。")
|
||||
confidence: float = Field(description="置信度。")
|
||||
evidence_ids: list[str] = Field(default_factory=list, description="证据 import_item ID。")
|
||||
status: str = Field(description="审核状态,契约字典 §10.6。")
|
||||
reason: str | None = Field(default=None)
|
||||
reviewed_at: str | None = Field(default=None)
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
class MemoryCandidateResponse(BaseModel):
|
||||
id: str = Field(description="候选 ID,前缀 memory_candidate_。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
import_job_id: str | None = Field(default=None, description="来源导入任务 ID。")
|
||||
cycle_id: str | None = Field(default=None, description="来源大周天循环 ID。")
|
||||
type: str = Field(description="记忆类型。")
|
||||
title: str = Field(description="记忆标题。")
|
||||
content: str = Field(description="记忆内容。")
|
||||
confidence: float = Field(description="置信度。")
|
||||
importance: float = Field(description="重要度。")
|
||||
sensitivity: str = Field(description="敏感等级。")
|
||||
evidence_ids: list[str] = Field(default_factory=list, description="证据 import_item ID。")
|
||||
status: str = Field(description="审核状态,契约字典 §10.6。")
|
||||
reason: str | None = Field(default=None)
|
||||
reviewed_at: str | None = Field(default=None)
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
class CandidateReviewDecision(BaseModel):
|
||||
candidate_id: str = Field(description="候选 ID。")
|
||||
decision: str = Field(description="审核决定:approve / reject。")
|
||||
|
||||
|
||||
class BatchReviewRequest(BaseModel):
|
||||
decisions: list[CandidateReviewDecision] = Field(min_length=1, description="批量审核决定。")
|
||||
|
||||
|
||||
class BatchReviewResult(BaseModel):
|
||||
applied: int = Field(description="已应用(approve)数量。")
|
||||
rejected: int = Field(description="已驳回数量。")
|
||||
skipped: int = Field(description="因状态非 pending 跳过的数量。")
|
||||
@@ -0,0 +1,137 @@
|
||||
"""人物 API schema。"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class CharacterResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"name": "default",
|
||||
"display_name": "魂镜默认人物",
|
||||
"status": "active",
|
||||
"language": "zh-CN",
|
||||
"profile": {"bio": "本地 MVP 默认人物。"},
|
||||
"model_policy": {"default_model_hint": "deepseek-v4-flash"},
|
||||
"current_personality_version_id": "personality_9b5f5b7e413442d0a2f9e61a2f4d7df5",
|
||||
"privacy_level": "normal",
|
||||
"owner_scope": "local-user",
|
||||
"is_system_template": False,
|
||||
"source_template_id": "character_template_default",
|
||||
"created_at": "2026-06-19T09:00:00.000000Z",
|
||||
"updated_at": "2026-06-19T09:00:00.000000Z",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
id: str = Field(description="人物 ID,前缀为 character_。")
|
||||
name: str = Field(description="人物内部名称。")
|
||||
display_name: str | None = Field(description="人物显示名称。")
|
||||
status: str = Field(description="人物状态,取值见契约字典 character.status。")
|
||||
language: str = Field(description="人物默认语言。")
|
||||
profile: dict[str, Any] = Field(description="人物档案 JSON。")
|
||||
model_policy: dict[str, Any] = Field(description="人物模型策略。")
|
||||
current_personality_version_id: str | None = Field(description="当前人格版本 ID。")
|
||||
privacy_level: str = Field(description="隐私等级:normal / sensitive / local_only。")
|
||||
owner_scope: str = Field(description="归属范围;当前本地 MVP 为 local-user。")
|
||||
is_system_template: bool = Field(description="是否系统人物模板;active API 通常返回 false。")
|
||||
source_template_id: str | None = Field(
|
||||
description="来源系统模板人物 ID;非模板复制人物通常有值。"
|
||||
)
|
||||
avatar_url: str | None = Field(
|
||||
default=None, description="角色头像 URL。无头像时为 null。"
|
||||
)
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
class CharacterSummaryResponse(BaseModel):
|
||||
"""人物列表项(api-spec §6 列出人物)。"""
|
||||
|
||||
id: str = Field(description="人物 ID,前缀为 character_。")
|
||||
name: str = Field(description="人物内部名称。")
|
||||
display_name: str | None = Field(description="人物显示名称。")
|
||||
status: str = Field(description="人物状态,取值见契约字典 character.status。")
|
||||
memory_count: int = Field(default=0, description="人物关联的未删除记忆数量。")
|
||||
personality_version: int = Field(default=0, description="当前人格版本号;尚未形成人格时为 0。")
|
||||
activated_at: str | None = Field(
|
||||
default=None, description="最近激活时间,UTC ISO 8601 字符串。"
|
||||
)
|
||||
|
||||
|
||||
class CreateCharacterRequest(BaseModel):
|
||||
name: str = Field(min_length=1, description="人物内部名称,必填。")
|
||||
display_name: str | None = Field(default=None, description="可选显示昵称。")
|
||||
template_key: str | None = Field(
|
||||
default=None,
|
||||
description="系统模板 key;传此值则从模板创建(继承档案/模型策略,status=active)。",
|
||||
)
|
||||
profile: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="人物档案,可含 birthday/height/weight/background/speaking_style。",
|
||||
)
|
||||
model_policy: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="模型策略,可含 default_chat_model_hint / memory_extraction_model_hint 等。",
|
||||
)
|
||||
|
||||
|
||||
class UpdateProfileRequest(BaseModel):
|
||||
"""更新人物档案(api-spec §6 PATCH /characters/{id}/profile)。手动编辑视为用户确认。"""
|
||||
|
||||
display_name: str | None = Field(default=None, description="可选显示昵称。")
|
||||
birthday: str | None = Field(default=None)
|
||||
height: str | None = Field(default=None)
|
||||
weight: str | None = Field(default=None)
|
||||
description: str | None = Field(default=None, description="一句话简介。")
|
||||
background: str | None = Field(default=None)
|
||||
speaking_style: str | None = Field(default=None, description="表达风格。")
|
||||
temperament: str | None = Field(default=None, description="性情气质。")
|
||||
hobbies: list[str] | None = Field(default=None, description="兴趣爱好列表。")
|
||||
values: list[str] | None = Field(default=None, description="价值观列表。")
|
||||
|
||||
|
||||
class UpdateModelPolicyRequest(BaseModel):
|
||||
"""更新人物模型策略(api-spec §6 PATCH /characters/{id}/model-policy)。"""
|
||||
|
||||
default_chat_model_hint: str | None = Field(default=None)
|
||||
memory_extraction_model_hint: str | None = Field(default=None)
|
||||
personality_analysis_model_hint: str | None = Field(default=None)
|
||||
embedding_model_hint: str | None = Field(default=None)
|
||||
privacy_level: str | None = Field(default=None)
|
||||
|
||||
|
||||
class ActivateCharacterResponse(BaseModel):
|
||||
"""激活人物响应(api-spec §6 POST /characters/{id}/activate)。"""
|
||||
|
||||
active_character_id: str = Field(description="当前激活人物 ID。")
|
||||
status: str = Field(description="激活后状态,恒为 active。")
|
||||
|
||||
|
||||
class CharacterTemplateSummaryResponse(BaseModel):
|
||||
"""系统模板列表项(GET /character-templates)。"""
|
||||
|
||||
key: str = Field(description="模板唯一标识(template_key)。")
|
||||
name: str = Field(description="模板人物内部名称。")
|
||||
display_name: str | None = Field(description="模板人物显示昵称。")
|
||||
description: str | None = Field(description="档案中的一句话描述(profile.description)。")
|
||||
temperament: str | None = Field(description="档案中的性情气质(profile.temperament)。")
|
||||
speaking_style: str | None = Field(description="档案中的说话风格(profile.speaking_style)。")
|
||||
language: str = Field(description="默认语言。")
|
||||
|
||||
|
||||
class ImportCardResponse(BaseModel):
|
||||
"""角色卡导入结果。"""
|
||||
|
||||
character_id: str = Field(description="新建人物 ID。")
|
||||
name: str = Field(description="人物名称。")
|
||||
display_name: str | None = Field(default=None, description="显示名(V3 nickname 优先)。")
|
||||
lorebook_id: str | None = Field(
|
||||
default=None, description="导入的世谱 ID(character_book);无则为 null。"
|
||||
)
|
||||
entry_count: int = Field(default=0, description="世谱条目数。")
|
||||
@@ -0,0 +1,254 @@
|
||||
"""聊天 API schema。"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class CreateSessionRequest(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"title": "大模型联调会话",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
title: str | None = Field(default=None, max_length=200, description="会话标题。")
|
||||
character_id: str | None = Field(default=None, description="人物 ID;为空时使用当前激活人物。")
|
||||
|
||||
|
||||
class UpdateSessionRequest(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{"title": "新的会话标题"},
|
||||
{"status": "archived"},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
title: str | None = Field(default=None, max_length=200, description="新标题;为空时保持不变。")
|
||||
status: str | None = Field(
|
||||
default=None,
|
||||
description="会话状态切换:active / archived。deleted 请走 DELETE 端点。",
|
||||
)
|
||||
|
||||
|
||||
class ChatSessionResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"session_id": "session_2e7c3cbd4ef8472bab59718b7d012f80",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"title": "大模型联调会话",
|
||||
"status": "active",
|
||||
"created_at": "2026-06-19T09:06:47.515000Z",
|
||||
"updated_at": "2026-06-19T09:10:52.690000Z",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
session_id: str = Field(description="会话 ID,前缀为 session_。")
|
||||
character_id: str = Field(description="会话所属人物 ID。")
|
||||
title: str | None = Field(description="会话标题。")
|
||||
status: str = Field(description="会话状态:active / archived / deleted。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601 字符串。")
|
||||
message_count: int = Field(default=0, description="会话内未删除消息数(用户 + 助手)。")
|
||||
last_message_preview: str | None = Field(default=None, description="最近一条消息的内容摘要。")
|
||||
|
||||
|
||||
class ChatSessionListResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"session_id": "session_2e7c3cbd4ef8472bab59718b7d012f80",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"title": "大模型联调会话",
|
||||
"status": "active",
|
||||
"created_at": "2026-06-19T09:06:47.515000Z",
|
||||
"updated_at": "2026-06-19T09:10:52.690000Z",
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
items: list[ChatSessionResponse] = Field(description="当前页会话列表。")
|
||||
total: int = Field(description="符合条件的会话总数。")
|
||||
|
||||
|
||||
class SendMessageRequest(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"content": "你好,请简单介绍一下你自己。",
|
||||
"modality": "text",
|
||||
"model_hint": "deepseek-v4-flash",
|
||||
},
|
||||
{
|
||||
"content": "请用三句话总结我们刚才聊到的内容。",
|
||||
"modality": "text",
|
||||
"model_hint": "default-chat",
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
content: str = Field(min_length=1, description="用户消息正文。")
|
||||
modality: str = Field(default="text", description="消息模态;当前阶段仅支持 text。")
|
||||
model_hint: str | None = Field(
|
||||
default=None,
|
||||
description="逻辑模型名或能力槽位,例如 default-chat、deepseek-v4-flash;为空时使用人物默认模型。",
|
||||
)
|
||||
thinking: bool = Field(
|
||||
default=False, description="是否开启思考模式(reasoning);模型不支持时忽略。"
|
||||
)
|
||||
reasoning_effort: str | None = Field(
|
||||
default=None,
|
||||
description="思考力度:low / medium / high。thinking=false 时忽略,使用模型默认。",
|
||||
)
|
||||
prior_versions: list[dict] | None = Field(
|
||||
default=None,
|
||||
description="重答时携带的历史版本列表,每项含 content 和 created_at。后端将其合并到新消息的 versions_json 中(前置插入),"
|
||||
"同时软删除前一条 assistant 消息,避免刷新后出现重复消息。",
|
||||
)
|
||||
|
||||
|
||||
class SendMessageResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"message_id": "message_462927da6f0e4af780dd28fd58f73314",
|
||||
"assistant_message_id": "message_2f9b7bf7d3b84231b67b28bb39fbfa7a",
|
||||
"run_id": "run_a2c8ecc73c0e4cc7bc2fafbaeada74e0",
|
||||
"session_id": "session_2e7c3cbd4ef8472bab59718b7d012f80",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"status": "completed",
|
||||
"stream_url": "/api/v1/runs/run_a2c8ecc73c0e4cc7bc2fafbaeada74e0/stream",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
message_id: str = Field(description="本次写入的用户消息 ID。")
|
||||
assistant_message_id: str | None = Field(description="模型回复消息 ID;失败时可能为空。")
|
||||
run_id: str = Field(description="本次对话运行 ID,可用于查询 Run 或 SSE。")
|
||||
session_id: str = Field(description="会话 ID。")
|
||||
character_id: str = Field(description="人物 ID。")
|
||||
status: str = Field(description="运行状态。成功完成时为 completed。")
|
||||
stream_url: str = Field(description="SSE 事件流 URL(前置于此,前端可直接 new EventSource)。")
|
||||
|
||||
|
||||
class ChatMessageResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"message_id": "message_2f9b7bf7d3b84231b67b28bb39fbfa7a",
|
||||
"session_id": "session_2e7c3cbd4ef8472bab59718b7d012f80",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"role": "assistant",
|
||||
"content": "你好,我是魂镜中的默认人物,会结合记忆和当前会话与你对话。",
|
||||
"modality": "text",
|
||||
"run_id": "run_a2c8ecc73c0e4cc7bc2fafbaeada74e0",
|
||||
"metadata": {"model_hint": "deepseek-v4-flash", "provider": "deepseek"},
|
||||
"sensitivity": "low",
|
||||
"created_at": "2026-06-19T09:10:52.676462Z",
|
||||
"deleted_at": None,
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
message_id: str = Field(description="消息 ID,前缀为 message_。")
|
||||
session_id: str = Field(description="所属会话 ID。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
role: str = Field(description="消息角色:user / assistant / system / tool。")
|
||||
content: str = Field(description="消息正文。")
|
||||
modality: str = Field(description="消息模态:text / image / audio / file。")
|
||||
run_id: str | None = Field(description="关联运行 ID。")
|
||||
metadata: dict[str, Any] = Field(description="消息元数据,例如模型名、provider 等。")
|
||||
sensitivity: str = Field(description="敏感等级:none / low / medium / high。")
|
||||
versions: list[dict] = Field(
|
||||
default=[],
|
||||
description="版本历史列表,每项含 content 和 created_at。",
|
||||
)
|
||||
active_version_idx: int = Field(
|
||||
default=0,
|
||||
description="当前活跃版本在 versions 中的索引。",
|
||||
)
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
deleted_at: str | None = Field(description="删除时间;未删除时为空。")
|
||||
|
||||
|
||||
class ChatMessageListResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"message_id": "message_462927da6f0e4af780dd28fd58f73314",
|
||||
"session_id": "session_2e7c3cbd4ef8472bab59718b7d012f80",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"role": "user",
|
||||
"content": "你好,请简单介绍一下你自己。",
|
||||
"modality": "text",
|
||||
"run_id": "run_a2c8ecc73c0e4cc7bc2fafbaeada74e0",
|
||||
"metadata": {},
|
||||
"sensitivity": "low",
|
||||
"created_at": "2026-06-19T09:10:52.660153Z",
|
||||
"deleted_at": None,
|
||||
},
|
||||
{
|
||||
"message_id": "message_2f9b7bf7d3b84231b67b28bb39fbfa7a",
|
||||
"session_id": "session_2e7c3cbd4ef8472bab59718b7d012f80",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"role": "assistant",
|
||||
"content": "你好,我是魂镜中的默认人物,会结合记忆和当前会话与你对话。",
|
||||
"modality": "text",
|
||||
"run_id": "run_a2c8ecc73c0e4cc7bc2fafbaeada74e0",
|
||||
"metadata": {"model_hint": "deepseek-v4-flash", "provider": "deepseek"},
|
||||
"sensitivity": "low",
|
||||
"created_at": "2026-06-19T09:10:52.676462Z",
|
||||
"deleted_at": None,
|
||||
},
|
||||
],
|
||||
"total": 2,
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
items: list[ChatMessageResponse] = Field(description="按创建时间升序排列的消息列表。")
|
||||
total: int = Field(description="符合条件的消息总数。")
|
||||
|
||||
|
||||
class SelectVersionRequest(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{"version_idx": 0},
|
||||
{"version_idx": 2},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
version_idx: int = Field(
|
||||
ge=0,
|
||||
description="要激活的版本在消息 versions 数组中的索引(0-based)。",
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
"""通用响应模型。"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class ErrorBody(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"code": "model_error",
|
||||
"message": "模型 provider 返回错误。",
|
||||
"trace_id": "trace_e5e220f8656c488895a696a5e9b5eedd",
|
||||
"details": {"status_code": 400, "body": "Model Not Exist"},
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
code: str = Field(description="标准错误码,取值见契约字典 API 标准错误码。")
|
||||
message: str = Field(description="面向用户的错误信息。")
|
||||
trace_id: str = Field(description="链路追踪 ID。")
|
||||
details: dict[str, Any] = Field(default_factory=dict, description="脱敏后的错误详情。")
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"error": {
|
||||
"code": "not_found",
|
||||
"message": "会话不存在。",
|
||||
"trace_id": "trace_e5e220f8656c488895a696a5e9b5eedd",
|
||||
"details": {},
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
error: ErrorBody = Field(description="错误响应主体。")
|
||||
|
||||
|
||||
class Page[T](BaseModel):
|
||||
items: list[T] = Field(description="当前页数据。")
|
||||
page: int = Field(description="页码,从 1 开始。")
|
||||
page_size: int = Field(description="每页数量。")
|
||||
total: int = Field(description="总数量。")
|
||||
|
||||
|
||||
class OrmModel(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -0,0 +1,41 @@
|
||||
"""内驱力 API schema(database-schema §22;/drive 端点)。"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from app.schemas.common import OrmModel
|
||||
|
||||
|
||||
class CuriosityResponse(OrmModel):
|
||||
"""好奇问题响应(related_entities 已从 JSON 文本解析)。"""
|
||||
|
||||
id: str
|
||||
character_id: str
|
||||
cycle_id: str
|
||||
question: str
|
||||
source: str
|
||||
related_entities: list[str] = Field(default_factory=list)
|
||||
expected_value: float
|
||||
urgency: float
|
||||
risk_level: str
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class SubgoalResponse(OrmModel):
|
||||
"""探索性次生目标响应。"""
|
||||
|
||||
id: str
|
||||
character_id: str
|
||||
cycle_id: str
|
||||
parent_goal_id: str | None = None
|
||||
curiosity_question_id: str
|
||||
title: str
|
||||
description: str
|
||||
expected_information_gain: float
|
||||
interruption_cost: float
|
||||
risk_level: str
|
||||
status: str
|
||||
reviewed_at: datetime | None = None
|
||||
reviewed_by: str
|
||||
created_at: datetime
|
||||
@@ -0,0 +1,35 @@
|
||||
"""演化候选 API schema(契约字典 §12;api-spec 演化候选审核)。"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from app.schemas.common import OrmModel
|
||||
|
||||
|
||||
class EvolutionCandidateResponse(OrmModel):
|
||||
"""演化候选响应(delta/evidence_ids 已从 JSON 文本解析)。"""
|
||||
|
||||
id: str = Field(description="演化候选 ID(evolution_ 前缀)")
|
||||
character_id: str
|
||||
cycle_id: str = Field(description="触发本次的大周天循环 ID")
|
||||
type: str = Field(description="候选类型,取值见契约字典 §12.1")
|
||||
source: str = Field(description="来源,本期 big_cycle")
|
||||
level: str = Field(description="影响层级 L0-L4")
|
||||
scope: str = Field(description="作用域,取值见契约字典 §12.3")
|
||||
title: str
|
||||
proposal: str
|
||||
delta: dict[str, Any] = Field(default_factory=dict, description="演化增量")
|
||||
evidence_ids: list[str] = Field(default_factory=list)
|
||||
confidence: float
|
||||
benefit_score: float
|
||||
risk_score: float
|
||||
drift_score: float
|
||||
auto_apply_allowed: bool
|
||||
status: str
|
||||
resulting_target_id: str | None = None
|
||||
error_message: str | None = None
|
||||
reviewed_at: datetime | None = None
|
||||
reviewed_by: str
|
||||
created_at: datetime
|
||||
@@ -0,0 +1,45 @@
|
||||
"""资料导入 API schema(api-spec §8)。"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class CreateImportRequest(BaseModel):
|
||||
"""JSON 提交导入(粘贴文本 / 结构化消息 / 手动设定)。"""
|
||||
|
||||
material_type: str = Field(description="资料类型,取值见契约字典 §10.1。")
|
||||
format: str = Field(default="text", description="格式:text / json / markdown 等。")
|
||||
content: str = Field(min_length=1, description="资料正文。")
|
||||
title: str | None = Field(default=None, description="可选标题。")
|
||||
source_note: str | None = Field(default=None, description="资料来源说明。")
|
||||
|
||||
|
||||
class ImportItemResponse(BaseModel):
|
||||
id: str = Field(description="条目 ID,前缀 import_item_。")
|
||||
import_job_id: str = Field(description="所属导入任务 ID。")
|
||||
item_type: str = Field(description="条目类型,契约字典 §10.4。")
|
||||
role: str = Field(description="条目角色,契约字典 §10.5。")
|
||||
sequence: int = Field(description="条目序号。")
|
||||
content: str = Field(description="条目正文。")
|
||||
metadata: dict[str, Any] = Field(default_factory=dict, description="条目附加元数据。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
class ImportJobResponse(BaseModel):
|
||||
id: str = Field(description="导入任务 ID,前缀 import_。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
material_type: str = Field(description="资料类型。")
|
||||
source_type: str = Field(description="导入来源类型,契约字典 §10.2。")
|
||||
title: str | None = Field(default=None)
|
||||
status: str = Field(description="任务状态,契约字典 §10.3。")
|
||||
format: str = Field(description="资料格式。")
|
||||
media_type: str | None = Field(default=None)
|
||||
mime_type: str | None = Field(default=None)
|
||||
item_count: int = Field(description="解析出的条目数。")
|
||||
consent_status: str = Field(description="授权状态。")
|
||||
usage_scope: str = Field(description="用途范围。")
|
||||
error_code: str | None = Field(default=None)
|
||||
error_message: str | None = Field(default=None)
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601 字符串。")
|
||||
@@ -0,0 +1,77 @@
|
||||
"""世谱 API schema。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class LorebookEntryBody(BaseModel):
|
||||
keys: list[str] = Field(default_factory=list)
|
||||
content: str = ""
|
||||
secondary_keys: list[str] = Field(default_factory=list)
|
||||
constant: bool = False
|
||||
selective: bool = False
|
||||
enabled: bool = True
|
||||
case_sensitive: bool = False
|
||||
use_regex: bool = False
|
||||
position: str = "after_char"
|
||||
insertion_order: int = 100
|
||||
priority: int = 10
|
||||
comment: str = ""
|
||||
bound_entity_id: str | None = None
|
||||
|
||||
|
||||
class LorebookEntryCreateRequest(LorebookEntryBody):
|
||||
pass
|
||||
|
||||
|
||||
class LorebookEntryUpdateRequest(BaseModel):
|
||||
keys: list[str] | None = None
|
||||
content: str | None = None
|
||||
secondary_keys: list[str] | None = None
|
||||
constant: bool | None = None
|
||||
selective: bool | None = None
|
||||
enabled: bool | None = None
|
||||
case_sensitive: bool | None = None
|
||||
use_regex: bool | None = None
|
||||
position: str | None = None
|
||||
insertion_order: int | None = None
|
||||
priority: int | None = None
|
||||
comment: str | None = None
|
||||
bound_entity_id: str | None = None
|
||||
|
||||
|
||||
class LorebookEntryResponse(LorebookEntryBody):
|
||||
id: str
|
||||
lorebook_id: str
|
||||
|
||||
|
||||
class LorebookCreateRequest(BaseModel):
|
||||
name: str = ""
|
||||
description: str = ""
|
||||
character_id: str | None = None # None + scope=global 表示全局世谱
|
||||
scope: str = "character"
|
||||
scan_depth: int | None = None
|
||||
token_budget: int | None = None
|
||||
recursive_scanning: bool = False
|
||||
|
||||
|
||||
class LorebookUpdateRequest(BaseModel):
|
||||
name: str | None = None
|
||||
description: str | None = None
|
||||
scan_depth: int | None = None
|
||||
token_budget: int | None = None
|
||||
recursive_scanning: bool | None = None
|
||||
|
||||
|
||||
class LorebookResponse(BaseModel):
|
||||
id: str
|
||||
character_id: str | None
|
||||
name: str
|
||||
description: str
|
||||
scope: str
|
||||
scan_depth: int | None
|
||||
token_budget: int | None
|
||||
recursive_scanning: bool
|
||||
status: str
|
||||
entries: list[LorebookEntryResponse] = Field(default_factory=list)
|
||||
@@ -0,0 +1,83 @@
|
||||
"""MCP server API schema(skills-design §5)。
|
||||
|
||||
支持两种传输协议:streamable-http / stdio。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.schemas.common import OrmModel
|
||||
|
||||
|
||||
class CreateMcpServerRequest(BaseModel):
|
||||
name: str = Field(min_length=1)
|
||||
transport: str = Field(
|
||||
default="streamable-http",
|
||||
description="传输协议:streamable-http | stdio",
|
||||
)
|
||||
# streamable-http 用
|
||||
endpoint_url: str | None = None
|
||||
auth_token: str | None = None
|
||||
# stdio 用
|
||||
command: str | None = Field(
|
||||
default=None,
|
||||
description="stdio 可执行文件路径,如 npx",
|
||||
)
|
||||
args_json: str | None = Field(
|
||||
default=None,
|
||||
description='启动参数 JSON list,如 \'["-y","@modelcontextprotocol/server-filesystem"]\'',
|
||||
)
|
||||
env_json: str | None = Field(
|
||||
default=None,
|
||||
description="环境变量 JSON dict",
|
||||
)
|
||||
cwd: str | None = Field(default=None, description="工作目录")
|
||||
status: str = "enabled"
|
||||
|
||||
|
||||
class McpServerResponse(OrmModel):
|
||||
"""MCP server 响应(不暴露 auth_token 原文,仅 has_auth_token)。"""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
transport: str
|
||||
endpoint_url: str | None
|
||||
has_auth_token: bool = False
|
||||
command: str | None
|
||||
args_json: str | None
|
||||
env_json: str | None
|
||||
cwd: str | None
|
||||
status: str
|
||||
tools: list[dict[str, Any]] = Field(default_factory=list)
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class UpdateMcpServerRequest(BaseModel):
|
||||
"""更新 MCP server(部分字段)。"""
|
||||
|
||||
status: str | None = Field(
|
||||
default=None,
|
||||
description="启用状态:enabled | disabled",
|
||||
)
|
||||
endpoint_url: str | None = None
|
||||
auth_token: str | None = None
|
||||
command: str | None = None
|
||||
args_json: str | None = None
|
||||
env_json: str | None = None
|
||||
cwd: str | None = None
|
||||
|
||||
|
||||
class McpServerStatusResponse(BaseModel):
|
||||
"""MCP server 进程状态。"""
|
||||
|
||||
server_id: str
|
||||
name: str
|
||||
transport: str
|
||||
status: str # running | stopped | error
|
||||
pid: int | None = None
|
||||
uptime_seconds: float | None = None
|
||||
@@ -0,0 +1,109 @@
|
||||
"""记忆 API schema。"""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class MemoryCreateRequest(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"type": "user",
|
||||
"title": "回答偏好",
|
||||
"content": "用户喜欢简洁、直接、带验证结果的中文回答。",
|
||||
"source": "user_explicit",
|
||||
"confidence": 1.0,
|
||||
"importance": 0.8,
|
||||
"sensitivity": "low",
|
||||
"evidence_ids": ["message_462927da6f0e4af780dd28fd58f73314"],
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
type: str = Field(
|
||||
default="user", max_length=60, description="记忆类型,取值见契约字典 memory.type。"
|
||||
)
|
||||
title: str = Field(max_length=200, description="记忆标题。")
|
||||
content: str = Field(min_length=1, description="记忆正文。")
|
||||
source: str = Field(
|
||||
default="user_explicit", description="记忆来源,取值见契约字典 memory.source。"
|
||||
)
|
||||
confidence: float = Field(default=1.0, ge=0.0, le=1.0, description="置信度,范围 0.0–1.0。")
|
||||
importance: float = Field(default=0.5, ge=0.0, le=1.0, description="重要性,范围 0.0–1.0。")
|
||||
sensitivity: str = Field(default="low", description="敏感等级:none / low / medium / high。")
|
||||
evidence_ids: list[str] = Field(default_factory=list, description="证据 ID 列表。")
|
||||
character_id: str | None = Field(default=None, description="人物 ID;为空时使用当前激活人物。")
|
||||
|
||||
|
||||
class MemoryUpdateRequest(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"title": "回答偏好(已确认)",
|
||||
"importance": 0.9,
|
||||
"status": "active",
|
||||
"evidence_ids": ["message_462927da6f0e4af780dd28fd58f73314"],
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
type: str | None = Field(default=None, max_length=60, description="记忆类型。")
|
||||
title: str | None = Field(default=None, max_length=200, description="记忆标题。")
|
||||
content: str | None = Field(default=None, description="记忆正文。")
|
||||
confidence: float | None = Field(
|
||||
default=None, ge=0.0, le=1.0, description="置信度,范围 0.0–1.0。"
|
||||
)
|
||||
importance: float | None = Field(
|
||||
default=None, ge=0.0, le=1.0, description="重要性,范围 0.0–1.0。"
|
||||
)
|
||||
sensitivity: str | None = Field(default=None, description="敏感等级。")
|
||||
status: str | None = Field(
|
||||
default=None, description="记忆状态:active / archived / deleted / superseded。"
|
||||
)
|
||||
evidence_ids: list[str] | None = Field(default=None, description="证据 ID 列表。")
|
||||
|
||||
|
||||
class MemoryResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"id": "memory_4d9c6e3b18bb4ef5a6b8ed2e902d1f1a",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"type": "user",
|
||||
"title": "回答偏好",
|
||||
"content": "用户喜欢简洁、直接、带验证结果的中文回答。",
|
||||
"source": "user_explicit",
|
||||
"confidence": 1.0,
|
||||
"importance": 0.8,
|
||||
"sensitivity": "low",
|
||||
"evidence_ids": ["message_462927da6f0e4af780dd28fd58f73314"],
|
||||
"status": "active",
|
||||
"created_at": "2026-06-19T09:12:00.000000Z",
|
||||
"updated_at": "2026-06-19T09:12:00.000000Z",
|
||||
"deleted_at": None,
|
||||
"expires_at": None,
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
id: str = Field(description="记忆 ID,前缀为 memory_。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
type: str = Field(description="记忆类型。")
|
||||
title: str = Field(description="记忆标题。")
|
||||
content: str = Field(description="记忆正文。")
|
||||
source: str = Field(description="记忆来源。")
|
||||
confidence: float = Field(description="置信度。")
|
||||
importance: float = Field(description="重要性。")
|
||||
sensitivity: str = Field(description="敏感等级。")
|
||||
evidence_ids: list[str] = Field(description="证据 ID 列表。")
|
||||
status: str = Field(description="记忆状态。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601 字符串。")
|
||||
deleted_at: str | None = Field(description="删除时间;未删除时为空。")
|
||||
expires_at: str | None = Field(description="过期时间;永不过期时为空。")
|
||||
@@ -0,0 +1,101 @@
|
||||
"""人格 API schema。"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class PersonalityResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"id": "personality_9b5f5b7e413442d0a2f9e61a2f4d7df5",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"version": 1,
|
||||
"identity_profile": "魂镜默认人物,面向本地 MVP 联调。",
|
||||
"role_destiny": "帮助用户验证对话、记忆和运行时链路。",
|
||||
"personality_seed": "稳定、简洁、可靠。",
|
||||
"traits": {"tone": "calm", "helpfulness": 0.9},
|
||||
"expression_style": "使用简洁中文回答,必要时列出验证步骤。",
|
||||
"values": {"accuracy": "high", "privacy": "high"},
|
||||
"relationship_pattern": "以协作者方式与用户互动。",
|
||||
"emotion_baseline": {"mood": "neutral"},
|
||||
"reason": "初始化默认人格。",
|
||||
"evidence_ids": [],
|
||||
"created_by": "system",
|
||||
"created_at": "2026-06-19T09:00:00.000000Z",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
id: str = Field(description="人格版本 ID。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
version: int = Field(description="人格版本号。")
|
||||
identity_profile: str = Field(description="身份画像。")
|
||||
role_destiny: str = Field(description="角色定位与使命。")
|
||||
personality_seed: str = Field(description="人格种子。")
|
||||
traits: dict[str, Any] = Field(description="人格特质。")
|
||||
expression_style: str = Field(description="表达风格。")
|
||||
values: dict[str, Any] = Field(description="价值偏好。")
|
||||
relationship_pattern: str = Field(description="关系模式。")
|
||||
emotion_baseline: dict[str, Any] = Field(description="情绪基线。")
|
||||
reason: str = Field(description="版本创建原因。")
|
||||
evidence_ids: list[str] = Field(description="证据 ID 列表。")
|
||||
created_by: str = Field(description="创建者。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
# 允许人工提议覆盖的人格字段(None 表示不改)。dict 字段落库为 JSON 文本。
|
||||
_PERSONALITY_STRING_FIELDS = (
|
||||
"identity_profile",
|
||||
"role_destiny",
|
||||
"personality_seed",
|
||||
"expression_style",
|
||||
"relationship_pattern",
|
||||
)
|
||||
_PERSONALITY_JSON_FIELDS = ("traits", "values", "emotion_baseline")
|
||||
|
||||
|
||||
class PersonalityProposalFields(BaseModel):
|
||||
"""人格字段覆盖建议;仅列允许编辑字段,未传(None)表示不改。"""
|
||||
|
||||
identity_profile: str | None = None
|
||||
role_destiny: str | None = None
|
||||
personality_seed: str | None = None
|
||||
traits: dict[str, Any] | None = None
|
||||
expression_style: str | None = None
|
||||
values: dict[str, Any] | None = None
|
||||
relationship_pattern: str | None = None
|
||||
emotion_baseline: dict[str, Any] | None = None
|
||||
|
||||
def has_any(self) -> bool:
|
||||
"""是否至少覆盖一个字段。"""
|
||||
return any(
|
||||
getattr(self, f) is not None
|
||||
for f in _PERSONALITY_STRING_FIELDS + _PERSONALITY_JSON_FIELDS
|
||||
)
|
||||
|
||||
|
||||
class ProposePersonalityUpdateRequest(BaseModel):
|
||||
"""提交人格修改建议(api-spec §11);建议进入 pending 待审核。"""
|
||||
|
||||
character_id: str | None = Field(default=None, description="人物 ID,省略取当前激活人物。")
|
||||
fields: PersonalityProposalFields = Field(description="需变更的人格字段覆盖。")
|
||||
reason: str = Field(min_length=1, description="修改原因,必填留痕。")
|
||||
|
||||
|
||||
class PersonalityProposalResponse(BaseModel):
|
||||
"""人格修改建议响应(api-spec §11)。"""
|
||||
|
||||
proposal_id: str = Field(description="建议 ID。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
status: str = Field(description="建议状态,见契约字典 §10.6 candidate.status。")
|
||||
reason: str = Field(description="修改原因。")
|
||||
fields: dict[str, Any] = Field(description="实际覆盖的字段集合。")
|
||||
resulting_version_id: str | None = Field(
|
||||
default=None, description="审核通过后生成的人格版本 ID。"
|
||||
)
|
||||
reviewed_at: str | None = Field(default=None, description="审核时间。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
@@ -0,0 +1,134 @@
|
||||
"""设置 API schema(模型网关与生成偏好)。
|
||||
|
||||
前端只见逻辑模型 / provider 密钥脱敏状态 / 生成参数偏好;
|
||||
真实 model 名、endpoint URL、API Key 由后端持有,不进入响应(environment-config §3)。
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class ModelEndpointStatus(BaseModel):
|
||||
"""endpoint 池状态。base_url / api_key 原值不回传(脱敏),仅回覆盖标记 + env 变量名。"""
|
||||
|
||||
name: str = Field(description="endpoint 名称(如 primary / fallback)。")
|
||||
provider: str = Field(description="所属 provider。")
|
||||
status: str = Field(description="endpoint 状态:active / inactive / unhealthy / cooldown。")
|
||||
weight: int | None = Field(default=None, description="权重(weighted_random 路由用)。")
|
||||
priority: int | None = Field(
|
||||
default=None, description="优先级(priority_failover 用,数值越小越优先)。"
|
||||
)
|
||||
is_overridden: bool = Field(default=False, description="是否存在 DB 覆盖。")
|
||||
has_url_override: bool = Field(
|
||||
default=False, description="是否用 DB 覆盖了 base_url(优先于 .env)。"
|
||||
)
|
||||
has_key_override: bool = Field(
|
||||
default=False, description="是否用 DB 覆盖了 api_key(优先于 .env)。"
|
||||
)
|
||||
base_url_env: str | None = Field(
|
||||
default=None, description="base_url 的 .env 变量名(只读展示)。"
|
||||
)
|
||||
api_key_env: str | None = Field(
|
||||
default=None, description="api_key 的 .env 变量名(只读展示)。"
|
||||
)
|
||||
|
||||
|
||||
class ProviderKeyStatus(BaseModel):
|
||||
"""供应商密钥脱敏状态:configured 表示 .env 是否已配置该 provider 的 API Key。"""
|
||||
|
||||
provider: str = Field(description="provider 标识。")
|
||||
configured: bool = Field(description="是否已配置 API Key(仅 true/false,绝不暴露值)。")
|
||||
|
||||
|
||||
class LogicalModel(BaseModel):
|
||||
"""逻辑模型(契约字典 §16)。业务层 model_hint 引用 name。"""
|
||||
|
||||
name: str = Field(description="逻辑模型名,业务层 model_hint 引用。")
|
||||
display_name: str = Field(description="展示名。")
|
||||
provider: str = Field(description="默认 provider。")
|
||||
capabilities: list[str] = Field(
|
||||
description="能力清单:chat / tool_use / structured_output / reasoning / long_context / vision。"
|
||||
)
|
||||
routing_strategy: str = Field(description="路由策略。")
|
||||
supports_streaming: bool = Field(description="是否支持流式。")
|
||||
supports_thinking: bool = Field(description="是否支持思考。")
|
||||
supports_structured_output: bool = Field(description="是否支持结构化输出。")
|
||||
context_window: int = Field(description="上下文窗口大小。")
|
||||
max_output_tokens: int | None = Field(default=None, description="最大输出 token 数。")
|
||||
endpoints: list[ModelEndpointStatus] | None = Field(
|
||||
default=None, description="endpoint 池脱敏状态。"
|
||||
)
|
||||
is_default: bool | None = Field(default=None, description="是否为默认对话模型槽位。")
|
||||
|
||||
|
||||
class ModelGatewayStatus(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"default_model_hint": "default-chat",
|
||||
"models": [],
|
||||
"providers": [{"provider": "deepseek", "configured": True}],
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
models: list[LogicalModel] = Field(description="逻辑模型清单。")
|
||||
providers: list[ProviderKeyStatus] = Field(description="provider 密钥脱敏状态。")
|
||||
default_model_hint: str = Field(description="默认逻辑模型名。")
|
||||
|
||||
|
||||
class GenerationPreferencesResponse(BaseModel):
|
||||
"""生成参数偏好(业务层,不含密钥)。"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"default_model_hint": "default-chat",
|
||||
"temperature": 0.7,
|
||||
"top_p": 1.0,
|
||||
"max_output_tokens": 4096,
|
||||
"streaming": True,
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
default_model_hint: str = Field(description="默认对话逻辑模型名。")
|
||||
temperature: float = Field(description="采样温度。")
|
||||
top_p: float = Field(description="核采样概率。")
|
||||
max_output_tokens: int = Field(description="最大输出 token 数。")
|
||||
streaming: bool = Field(description="是否启用流式输出。")
|
||||
|
||||
|
||||
class UpdateGenerationPreferencesRequest(BaseModel):
|
||||
"""更新生成偏好;至少传一个字段。"""
|
||||
|
||||
default_model_hint: str | None = Field(
|
||||
default=None, max_length=120, description="默认对话逻辑模型名。"
|
||||
)
|
||||
temperature: float | None = Field(default=None, ge=0.0, le=2.0, description="采样温度。")
|
||||
top_p: float | None = Field(default=None, ge=0.0, le=1.0, description="核采样概率。")
|
||||
max_output_tokens: int | None = Field(
|
||||
default=None, ge=1, le=200000, description="最大输出 token 数。"
|
||||
)
|
||||
streaming: bool | None = Field(default=None, description="是否启用流式输出。")
|
||||
|
||||
|
||||
class UpdateEndpointOverrideRequest(BaseModel):
|
||||
"""更新 endpoint 覆盖(PATCH /settings/models/{model}/endpoints/{endpoint})。
|
||||
|
||||
字段全可选;传 None 表示清除该字段覆盖、回退 yaml/env。base_url/api_key 经 DB
|
||||
覆盖优先于 .env。响应脱敏,不回 base_url/api_key 原值。
|
||||
"""
|
||||
|
||||
base_url: str | None = Field(
|
||||
default=None, description="覆盖 base_url(DB 优先 env);None 清除。"
|
||||
)
|
||||
api_key: str | None = Field(
|
||||
default=None, description="覆盖 api_key(DB 优先 env);None 清除。"
|
||||
)
|
||||
status: str | None = Field(default=None, description="覆盖 endpoint 状态。")
|
||||
weight: int | None = Field(default=None, ge=0, description="覆盖权重。")
|
||||
priority: int | None = Field(default=None, ge=0, description="覆盖优先级。")
|
||||
@@ -0,0 +1,110 @@
|
||||
"""运行时 API schema。"""
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class EventResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"event_id": "event_70d5ef8a4c5b44a893b4f83f933b78e9",
|
||||
"run_id": "run_a2c8ecc73c0e4cc7bc2fafbaeada74e0",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"type": "message.delta",
|
||||
"sequence": 2,
|
||||
"visibility": "public",
|
||||
"payload": {
|
||||
"message_id": "message_2f9b7bf7d3b84231b67b28bb39fbfa7a",
|
||||
"delta": "你好,我是魂镜中的默认人物,",
|
||||
"index": 0,
|
||||
},
|
||||
"created_at": "2026-06-19T09:10:52.676462Z",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
event_id: str = Field(description="事件 ID,前缀为 event_。")
|
||||
run_id: str = Field(description="所属运行 ID。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
type: str = Field(description="事件类型,例如 run.started、message.delta、run.completed。")
|
||||
sequence: int = Field(description="Run 内递增序号,用于 SSE 断点续传。")
|
||||
visibility: str = Field(description="事件可见性:public / internal / audit。")
|
||||
payload: dict[str, Any] = Field(
|
||||
description="事件负载。message.delta 中包含 delta、index、message_id。"
|
||||
)
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
trace_id: str | None = Field(
|
||||
default=None, description="链路追踪 ID,可在司命台链路追踪页据此追溯。"
|
||||
)
|
||||
|
||||
|
||||
class RunResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"id": "run_a2c8ecc73c0e4cc7bc2fafbaeada74e0",
|
||||
"character_id": "character_ec83d55746c54e47ab6f98d683032626",
|
||||
"session_id": "session_2e7c3cbd4ef8472bab59718b7d012f80",
|
||||
"status": "completed",
|
||||
"goal": "回复用户消息",
|
||||
"origin": "chat",
|
||||
"model_hint": "deepseek-v4-flash",
|
||||
"started_at": "2026-06-19T09:10:52.660153Z",
|
||||
"completed_at": "2026-06-19T09:10:52.690000Z",
|
||||
"error_code": None,
|
||||
"error_message": None,
|
||||
"created_at": "2026-06-19T09:10:52.660153Z",
|
||||
"updated_at": "2026-06-19T09:10:52.690000Z",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
id: str = Field(description="运行 ID,前缀为 run_。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
session_id: str | None = Field(description="关联会话 ID。")
|
||||
status: str = Field(
|
||||
description="运行状态:queued / running / completed / failed / cancelled / interrupted。"
|
||||
)
|
||||
goal: str | None = Field(description="运行目标。")
|
||||
origin: str = Field(description="运行来源,例如 chat。")
|
||||
model_hint: str | None = Field(description="本次运行使用的逻辑模型名或能力槽位。")
|
||||
started_at: str | None = Field(description="开始时间。")
|
||||
completed_at: str | None = Field(description="完成时间。")
|
||||
error_code: str | None = Field(description="失败错误码;成功时为空。")
|
||||
error_message: str | None = Field(description="失败错误信息;成功时为空。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
class AgentControlRequest(BaseModel):
|
||||
"""运行中 steer / follow-up 消息。"""
|
||||
|
||||
content: str = Field(min_length=1, max_length=20_000)
|
||||
|
||||
|
||||
class AgentControlResponse(BaseModel):
|
||||
accepted: bool = True
|
||||
|
||||
|
||||
class ConfirmationResolutionResponse(BaseModel):
|
||||
"""工具确认 resolve 结果(approve/deny 端点成功响应)。"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{"status": "resolved", "approved": True},
|
||||
{"status": "resolved", "approved": False},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
status: Literal["resolved"] = Field(
|
||||
default="resolved", description="恒为 resolved;失败路径由错误响应覆盖。"
|
||||
)
|
||||
approved: bool = Field(description="裁决结果:True=批准,False=拒绝。")
|
||||
@@ -0,0 +1,87 @@
|
||||
"""安全 / 工具策略 API schema(api-spec §15 / §16)。
|
||||
|
||||
`tool_policies` 表同时服务两组 API:
|
||||
- `/tools`(§15):返回 `ToolPolicyResponse`(完整元数据 + executable 派生位)。
|
||||
- `/safety/policies`(§16):返回 `SafetyPolicyItem`(策略子集)。
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class ToolPolicyResponse(BaseModel):
|
||||
"""工具策略完整元数据(GET /tools 元素、POST/PATCH /tools 响应)。"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
name: str = Field(description="工具名(主键)。")
|
||||
description: str = Field(default="", description="工具描述。")
|
||||
risk_level: str = Field(description="风险等级 L0–L4。")
|
||||
permission_policy: str = Field(description="权限策略 allow/ask/always_ask/forbidden。")
|
||||
side_effect: bool = Field(description="是否有副作用。")
|
||||
audit_required: bool = Field(description="是否强制记审计。")
|
||||
source: str = Field(description="来源:builtin / custom。")
|
||||
enabled: bool = Field(description="是否启用(禁用的工具不进入司命策略)。")
|
||||
executable: bool = Field(
|
||||
default=False,
|
||||
description="是否具备执行实现(builtin 或已注册 ToolSpec);纯治理声明为 false。",
|
||||
)
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601。")
|
||||
|
||||
|
||||
class SafetyPolicyItem(BaseModel):
|
||||
"""策略子集(GET /safety/policies 元素)。"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
tool: str = Field(description="工具名。")
|
||||
risk_level: str = Field(description="风险等级 L0–L4。")
|
||||
permission_policy: str = Field(description="权限策略。")
|
||||
side_effect: bool = Field(description="是否有副作用。")
|
||||
audit_required: bool = Field(description="是否强制记审计。")
|
||||
|
||||
|
||||
class CreateToolPolicyRequest(BaseModel):
|
||||
"""注册工具 / 新增策略请求(POST /tools、POST /safety/policies 共用)。"""
|
||||
|
||||
name: str = Field(description="工具名(唯一)。")
|
||||
description: str = Field(default="", description="工具描述。")
|
||||
risk_level: str = Field(default="L2", description="风险等级 L0–L4。")
|
||||
permission_policy: str = Field(default="ask", description="权限策略。")
|
||||
side_effect: bool = Field(default=True, description="是否有副作用。")
|
||||
audit_required: bool = Field(default=True, description="是否强制记审计。")
|
||||
enabled: bool = Field(default=True, description="是否启用。")
|
||||
|
||||
|
||||
class UpdateToolPolicyRequest(BaseModel):
|
||||
"""编辑工具 / 策略请求(PATCH /tools/{name}、PATCH /safety/policies/{tool} 共用)。
|
||||
|
||||
所有字段可选;spec §15 要求 risk_level、§16 要求 permission_policy / risk_level,
|
||||
此处统一放开为完整元数据编辑(至少传一个)。
|
||||
"""
|
||||
|
||||
description: str | None = None
|
||||
risk_level: str | None = None
|
||||
permission_policy: str | None = None
|
||||
side_effect: bool | None = None
|
||||
audit_required: bool | None = None
|
||||
enabled: bool | None = None
|
||||
|
||||
|
||||
class ToolCallDetail(BaseModel):
|
||||
"""单次工具调用事件详情(GET /tools/calls/{tool_call_id})。
|
||||
|
||||
`tool_call_id` 对应一炁事件存储中 tool.* 事件的主键;一次调用会产生 started /
|
||||
completed / failed 多个事件,每个均可单独查询。
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
event_id: str = Field(description="事件 ID(即路径 tool_call_id)。")
|
||||
type: str = Field(description="事件类型:tool.started / tool.completed / tool.failed 等。")
|
||||
tool: str | None = Field(default=None, description="工具名(payload 解出)。")
|
||||
run_id: str | None = Field(default=None)
|
||||
character_id: str | None = Field(default=None)
|
||||
trace_id: str | None = Field(default=None)
|
||||
payload: dict = Field(default_factory=dict, description="事件原始负载。")
|
||||
created_at: str = Field(description="事件时间,UTC ISO 8601。")
|
||||
@@ -0,0 +1,49 @@
|
||||
"""Skill API schema(skills-design §4.1)。"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.schemas.common import OrmModel
|
||||
|
||||
|
||||
class SkillResponse(OrmModel):
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
trigger: str
|
||||
prompt_fragment: str
|
||||
tool_bindings: list[str] = Field(default_factory=list)
|
||||
risk_level: str
|
||||
status: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class CreateSkillRequest(BaseModel):
|
||||
name: str = Field(min_length=1)
|
||||
description: str = ""
|
||||
trigger: str = ""
|
||||
prompt_fragment: str = ""
|
||||
tool_bindings: list[str] = Field(default_factory=list)
|
||||
risk_level: str = "L1"
|
||||
status: str = "enabled"
|
||||
|
||||
|
||||
class UpdateSkillRequest(BaseModel):
|
||||
description: str | None = None
|
||||
trigger: str | None = None
|
||||
prompt_fragment: str | None = None
|
||||
tool_bindings: list[str] | None = None
|
||||
risk_level: str | None = None
|
||||
status: str | None = None
|
||||
|
||||
|
||||
class ToolCapability(BaseModel):
|
||||
"""统一能力列举项(builtin / mcp / skill 三源)。"""
|
||||
|
||||
name: str
|
||||
description: str
|
||||
source: str # builtin / mcp / skill
|
||||
risk_level: str
|
||||
status: str
|
||||
@@ -0,0 +1,84 @@
|
||||
"""辰正时态 API schema(设计 §10)。对外字段不带 _json 后缀。"""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.modules.time_sense.modulation import ModulationFactors
|
||||
from app.modules.time_sense.snapshot import TemporalSnapshot
|
||||
|
||||
|
||||
class SnapshotResponse(BaseModel):
|
||||
"""GET /time/snapshot 响应:快照 + 因子 + context_text。"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
snapshot: TemporalSnapshot | None
|
||||
factors: ModulationFactors | None
|
||||
context_text: str
|
||||
|
||||
|
||||
class AnniversaryCreateRequest(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"name": "我们的相遇日",
|
||||
"calendar": "solar",
|
||||
"date": "2025-09-19",
|
||||
"recurs_yearly": True,
|
||||
"kind": "anniversary",
|
||||
"subject_type": "relationship",
|
||||
"source": "manual",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
name: str = Field(max_length=200, description="纪念日名称。")
|
||||
calendar: str = Field(description="历法:solar / lunar。")
|
||||
date: str = Field(description="日期:公历 ISO 或农历编码。")
|
||||
recurs_yearly: bool = Field(default=True, description="是否每年 recur。")
|
||||
kind: str = Field(description="类型,取值见契约 §11.4.2。")
|
||||
subject_type: str = Field(description="对象类型,取值见契约 §11.4.3。")
|
||||
source: str = Field(default="manual", description="来源,取值见契约 §11.4.4。")
|
||||
|
||||
|
||||
class AnniversaryUpdateRequest(BaseModel):
|
||||
name: str | None = Field(default=None, max_length=200)
|
||||
calendar: str | None = None
|
||||
date: str | None = None
|
||||
recurs_yearly: bool | None = None
|
||||
kind: str | None = None
|
||||
subject_type: str | None = None
|
||||
source: str | None = None
|
||||
|
||||
|
||||
class AnniversaryResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
name: str
|
||||
calendar: str
|
||||
date: str
|
||||
recurs_yearly: bool
|
||||
kind: str
|
||||
subject_type: str
|
||||
source: str
|
||||
|
||||
|
||||
class AcquaintanceResponse(BaseModel):
|
||||
total_days: int
|
||||
natural_language: str
|
||||
|
||||
|
||||
class TimeConfigResponse(BaseModel):
|
||||
"""GET /time/config:当前时态相关配置(只读,v1 经 env 覆盖)。"""
|
||||
|
||||
user_timezone: str
|
||||
home_timezone: str
|
||||
default_city: str
|
||||
offline_active_minutes: float
|
||||
offline_short_absence_minutes: float
|
||||
offline_long_absence_minutes: float
|
||||
offline_reunion_impact_minutes: float
|
||||
care_enabled: bool
|
||||
jetlag_threshold_minutes: int
|
||||
@@ -0,0 +1,61 @@
|
||||
"""世界模型 API schema(河图枢运行时,api-spec §12)。"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class WorldEntityCreateRequest(BaseModel):
|
||||
character_id: str | None = Field(default=None, description="所属人物;省略时用当前激活人物。")
|
||||
type: str = Field(default="concept", description="实体类型,取值见契约字典 §11.1。")
|
||||
name: str = Field(min_length=1, description="实体名称。")
|
||||
attributes: dict[str, Any] = Field(default_factory=dict, description="实体属性集合。")
|
||||
state_type: str = Field(default="inference", description="状态类型,取值见契约字典 §11.2。")
|
||||
confidence: float = Field(default=0.5, ge=0.0, le=1.0, description="置信度。")
|
||||
evidence_ids: list[str] = Field(default_factory=list, description="证据 ID 列表。")
|
||||
sources: list[str] = Field(default_factory=list, description="来源标识列表。")
|
||||
|
||||
|
||||
class WorldEntityResponse(BaseModel):
|
||||
id: str = Field(description="实体 ID,前缀 world_entity_。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
type: str = Field(description="实体类型。")
|
||||
name: str = Field(description="实体名称。")
|
||||
attributes: dict[str, Any] = Field(description="实体属性集合。")
|
||||
state_type: str = Field(description="状态类型。")
|
||||
status: str = Field(description="审核状态:pending/confirmed/rejected。")
|
||||
confidence: float = Field(description="置信度。")
|
||||
evidence_ids: list[str] = Field(description="证据 ID 列表。")
|
||||
sources: list[str] = Field(description="来源标识列表。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
class WorldRelationCreateRequest(BaseModel):
|
||||
character_id: str | None = Field(default=None, description="所属人物;省略时用当前激活人物。")
|
||||
subject_id: str = Field(description="关系主体实体 ID。")
|
||||
predicate: str = Field(
|
||||
min_length=1, description="关系谓词(自由字符串,如「位于」「依赖」「协助」)。"
|
||||
)
|
||||
object_id: str = Field(description="关系客体实体 ID。")
|
||||
confidence: float = Field(default=0.5, ge=0.0, le=1.0)
|
||||
evidence_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class WorldRelationResponse(BaseModel):
|
||||
id: str = Field(description="关系 ID,前缀 world_relation_。")
|
||||
character_id: str = Field(description="所属人物 ID。")
|
||||
subject_id: str = Field(description="关系主体实体 ID。")
|
||||
predicate: str = Field(description="关系谓词。")
|
||||
object_id: str = Field(description="关系客体实体 ID。")
|
||||
confidence: float = Field(description="置信度。")
|
||||
evidence_ids: list[str] = Field(description="证据 ID 列表。")
|
||||
created_at: str = Field(description="创建时间,UTC ISO 8601 字符串。")
|
||||
updated_at: str = Field(description="更新时间,UTC ISO 8601 字符串。")
|
||||
|
||||
|
||||
class WorldGraphResponse(BaseModel):
|
||||
"""世界图谱数据:所有节点与边,供前端 D3/Force 图谱渲染。"""
|
||||
|
||||
nodes: list[WorldEntityResponse] = Field(description="图谱节点(世界实体)。")
|
||||
edges: list[WorldRelationResponse] = Field(description="图谱边(实体间关系)。")
|
||||
Reference in new issue
Block a user