初始化提交

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

+72
View File
@@ -0,0 +1,72 @@
"""SQLAlchemy 模型导出。"""
from app.models.big_cycle import BigCycle
from app.models.candidates import MemoryCandidate, ProfileCandidate
from app.models.character import Character
from app.models.chat import ChatMessage, ChatSession
from app.models.drive import CuriosityQuestion, ExploratorySubgoal
from app.models.evolution import EvolutionCandidate
from app.models.governance import (
AgentRosterEntry,
GenerationPreferences,
Goal,
PerceptionChannel,
Plan,
WorldEntity,
)
from app.models.idempotency import IdempotencyKey
from app.models.imports import CharacterImportJob, ImportItem
from app.models.lorebook import Lorebook, LorebookEntry
from app.models.mcp_servers import McpServer
from app.models.memory import Memory, MemoryLink
from app.models.personality import PersonalityProposal, PersonalityVersion
from app.models.personality_emotion import CharacterEmotionState
from app.models.rbac import Permission, Role, RolePermission, UserRole
from app.models.runtime import Event, Run
from app.models.safety import AuditEvent, PendingConfirmationRecord
from app.models.skills import Skill
from app.models.time_anniversary import TimeAnniversary
from app.models.user import User
from app.models.world import CharacterWorldEntity, WorldRelation
__all__ = [
"AgentRosterEntry",
"AuditEvent",
"BigCycle",
"Character",
"CharacterEmotionState",
"CharacterImportJob",
"CharacterWorldEntity",
"ChatMessage",
"ChatSession",
"CuriosityQuestion",
"Event",
"EvolutionCandidate",
"ExploratorySubgoal",
"GenerationPreferences",
"Goal",
"IdempotencyKey",
"ImportItem",
"Lorebook",
"LorebookEntry",
"McpServer",
"Memory",
"MemoryCandidate",
"MemoryLink",
"PendingConfirmationRecord",
"PerceptionChannel",
"Permission",
"PersonalityProposal",
"PersonalityVersion",
"Plan",
"ProfileCandidate",
"Role",
"RolePermission",
"Run",
"Skill",
"TimeAnniversary",
"User",
"UserRole",
"WorldEntity",
"WorldRelation",
]
+38
View File
@@ -0,0 +1,38 @@
"""大周天运行记录(architecture §3.2;v1-scope §3.3)。
大周天是长期演化循环:记忆积累 → 自我评估 → 演化策略 → 候选生成。
本表记录每次循环的触发、阶段与终态;演化候选 / 内驱力 / 自动抽取由后续
子阶段写入各自表,循环本身只承载编排与留痕。
"""
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_cycle_id
from app.core.time import utc_now
from app.db.base import Base
class BigCycle(Base):
"""大周天循环运行记录。"""
__tablename__ = "big_cycles"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_cycle_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
trigger: Mapped[str] = mapped_column(String(40), nullable=False)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
phase: Mapped[str | None] = mapped_column(String(40))
memory_count_at_trigger: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
evaluation_snapshot_json: Mapped[str | None] = mapped_column(Text)
summary: Mapped[str | None] = mapped_column(Text)
error_code: Mapped[str | None] = mapped_column(String(80))
error_message: Mapped[str | None] = mapped_column(Text)
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+67
View File
@@ -0,0 +1,67 @@
"""资料导入候选模型(api-spec §8;契约字典 §10.6 candidate.status)。
导入资料经模型抽取产生档案 / 记忆候选,待用户审核后应用;人格建议复用
``personality_proposals``(005)。``evidence_ids`` 指向 ``import_items.id``
(EvidenceRef source_type = ``import_item``)。
"""
from datetime import datetime
from sqlalchemy import DateTime, Float, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_memory_candidate_id, new_profile_candidate_id
from app.core.time import utc_now
from app.db.base import Base
class ProfileCandidate(Base):
"""人物档案候选(契约字典 §10.6)。"""
__tablename__ = "character_profile_candidates"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_profile_candidate_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
import_job_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("character_import_jobs.id"), index=True
)
field: Mapped[str] = mapped_column(String(60), nullable=False)
value: Mapped[str] = mapped_column(Text, nullable=False)
confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
reason: Mapped[str | None] = mapped_column(Text)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class MemoryCandidate(Base):
"""记忆候选(契约字典 §10.6)。"""
__tablename__ = "memory_candidates"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_memory_candidate_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
import_job_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("character_import_jobs.id"), index=True
)
cycle_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("big_cycles.id"), index=True
)
type: Mapped[str] = mapped_column(String(60), nullable=False)
title: Mapped[str] = mapped_column(String(200), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
importance: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
sensitivity: Mapped[str] = mapped_column(String(40), nullable=False, default="low")
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
reason: Mapped[str | None] = mapped_column(Text)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+43
View File
@@ -0,0 +1,43 @@
"""人物模型。"""
from datetime import datetime
from sqlalchemy import Boolean, DateTime, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_character_id
from app.core.time import utc_now
from app.db.base import Base
class Character(Base):
__tablename__ = "characters"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_character_id)
name: Mapped[str] = mapped_column(String(120), nullable=False)
display_name: Mapped[str | None] = mapped_column(String(120))
status: Mapped[str] = mapped_column(String(40), nullable=False, default="active", index=True)
language: Mapped[str] = mapped_column(String(20), nullable=False, default="zh-CN")
profile_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
model_policy_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
safety_policy_id: Mapped[str | None] = mapped_column(String(80))
current_personality_version_id: Mapped[str | None] = mapped_column(String(80))
# 道体恒定层(设计理念 §2.1):不可运行时覆盖的人格内核快照,
# 由 personality_version 收敛 / 用户确认 / 酒馆卡降生写入,组装运行时契约时做完整性校验。
personality_summary: Mapped[str | None] = mapped_column(Text)
mission: Mapped[str | None] = mapped_column(Text)
safety_guidelines: Mapped[str | None] = mapped_column(Text)
current_mood_id: Mapped[str | None] = mapped_column(String(80))
privacy_level: Mapped[str] = mapped_column(String(40), nullable=False, default="normal")
owner_scope: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
is_system_template: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False, index=True
)
source_template_id: Mapped[str | None] = mapped_column(String(80), index=True)
template_key: Mapped[str | None] = mapped_column(String(80), index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
activated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
archived_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
+52
View File
@@ -0,0 +1,52 @@
"""聊天会话与消息模型。"""
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_message_id, new_session_id
from app.core.time import utc_now
from app.db.base import Base
class ChatSession(Base):
__tablename__ = "chat_sessions"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_session_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
title: Mapped[str | None] = mapped_column(String(200))
status: Mapped[str] = mapped_column(String(40), nullable=False, default="active", index=True)
owner_scope: Mapped[str] = mapped_column(String(80), nullable=False, default="", index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class ChatMessage(Base):
__tablename__ = "chat_messages"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_message_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
session_id: Mapped[str] = mapped_column(String(80), ForeignKey("chat_sessions.id"), index=True)
role: Mapped[str] = mapped_column(String(40), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
modality: Mapped[str] = mapped_column(String(40), nullable=False, default="text")
run_id: Mapped[str | None] = mapped_column(String(80), ForeignKey("runs.id"), index=True)
metadata_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
sensitivity: Mapped[str] = mapped_column(String(40), nullable=False, default="low")
versions_json: Mapped[str] = mapped_column(
Text,
nullable=False,
default="[]",
comment="版本历史 JSON 数组:[{content, created_at}],按生成顺序排列,末尾最新",
)
active_version_idx: Mapped[int] = mapped_column(
Integer,
nullable=False,
default=0,
comment="当前活跃版本在 versions_json 中的索引(0-based)",
)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
+54
View File
@@ -0,0 +1,54 @@
"""大周天内驱力模型(database-schema §22;intrinsic-drive §5)。
好奇问题记录「匮乏感」,探索性次生目标由好奇问题转化而来、可审核(candidate →
approved/deferred/rejected)。``cycle_id`` 关联触发本次的大周天循环。
"""
from datetime import datetime
from sqlalchemy import DateTime, Float, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_curiosity_id, new_subgoal_id
from app.core.time import utc_now
from app.db.base import Base
class CuriosityQuestion(Base):
"""好奇问题(缺口记录,无审核状态)。"""
__tablename__ = "curiosity_questions"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_curiosity_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
cycle_id: Mapped[str] = mapped_column(String(80), ForeignKey("big_cycles.id"), index=True)
question: Mapped[str] = mapped_column(Text, nullable=False)
source: Mapped[str] = mapped_column(String(60), nullable=False, default="reflection")
related_entities_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
expected_value: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
urgency: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
risk_level: Mapped[str] = mapped_column(String(8), nullable=False, default="L4")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
class ExploratorySubgoal(Base):
"""探索性次生目标(可审核,本期不自动执行)。"""
__tablename__ = "exploratory_subgoals"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_subgoal_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
cycle_id: Mapped[str] = mapped_column(String(80), ForeignKey("big_cycles.id"), index=True)
parent_goal_id: Mapped[str | None] = mapped_column(String(80))
curiosity_question_id: Mapped[str] = mapped_column(
String(80), ForeignKey("curiosity_questions.id"), index=True
)
title: Mapped[str] = mapped_column(String(200), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False)
expected_information_gain: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
interruption_cost: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
risk_level: Mapped[str] = mapped_column(String(8), nullable=False, default="L4")
status: Mapped[str] = mapped_column(String(40), nullable=False, default="candidate", index=True)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
reviewed_by: Mapped[str] = mapped_column(String(80), nullable=False, default="system")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
+50
View File
@@ -0,0 +1,50 @@
"""大周天演化候选模型(契约字典 §12;self-evolution-design.md §11)。
演化候选是大周天(及未来镜界 / 反馈)产出的统一轻量演化增量载体;``type`` 区分
候选对象,``level``(L0-L4)决定半自动边界(见 [大周天演化候选落地设计]
../../docs/domain/core-domains.md §5)。``cycle_id`` 关联触发本次
的大周天循环,``resulting_target_id`` 记录派生后的下游资源 ID。
"""
from datetime import datetime
from sqlalchemy import Boolean, DateTime, Float, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_evolution_candidate_id
from app.core.time import utc_now
from app.db.base import Base
class EvolutionCandidate(Base):
"""演化候选(统一表)。"""
__tablename__ = "evolution_candidates"
id: Mapped[str] = mapped_column(
String(80), primary_key=True, default=new_evolution_candidate_id
)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
cycle_id: Mapped[str] = mapped_column(String(80), ForeignKey("big_cycles.id"), index=True)
type: Mapped[str] = mapped_column(String(60), nullable=False)
source: Mapped[str] = mapped_column(String(60), nullable=False, default="big_cycle")
level: Mapped[str] = mapped_column(String(8), nullable=False, default="L4")
scope: Mapped[str] = mapped_column(String(40), nullable=False, default="character_scope")
title: Mapped[str] = mapped_column(String(200), nullable=False)
proposal: Mapped[str] = mapped_column(Text, nullable=False)
delta_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
benefit_score: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
risk_score: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
drift_score: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
auto_apply_allowed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
resulting_target_id: Mapped[str | None] = mapped_column(String(80))
error_message: Mapped[str | None] = mapped_column(Text)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
reviewed_by: Mapped[str] = mapped_column(String(80), nullable=False, default="system")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+197
View File
@@ -0,0 +1,197 @@
"""司命台治理对象模型(九枢治理视图,api-spec §19.4)。
目标(太一)/ 感知通道(烛照)/ 世界实体(河图)/ 计划(洛书)/ 天工阁角色
为可增长的开放集合;生成偏好(模型网关 §4.3)为本地单用户单行配置。
这些对象当前由司命台治理视图维护,对应枢的自动运行时暂未启用(见
docs/operations/capability-matrix.md §8)。
"""
from datetime import datetime
from sqlalchemy import (
Boolean,
DateTime,
Float,
ForeignKey,
Integer,
String,
Text,
)
from sqlalchemy.orm import Mapped, mapped_column
from app.core.config import settings
from app.core.ids import new_goal_id, new_plan_id, new_world_entity_id
from app.core.time import utc_now
from app.db.base import Base
class Goal(Base):
"""太一枢目标节点(契约字典 §5.11 / §5.12)。"""
__tablename__ = "goals"
def __init__(self, **kwargs: object) -> None:
# Python 端默认值:mapped_column(default=...) 仅在 ORM flush 时生效,
# __init__ 覆盖使测试在不侵入 session 时就能验证默认值。
kwargs.setdefault("type", "session")
kwargs.setdefault("progress", 0.0)
super().__init__(**kwargs)
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_goal_id)
character_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("characters.id"), nullable=True, index=True
)
title: Mapped[str] = mapped_column(String(300), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
level: Mapped[str] = mapped_column(String(40), nullable=False, default="task", index=True)
type: Mapped[str] = mapped_column(String(40), nullable=False, default="session")
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=50)
progress: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
constraints_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
parent_id: Mapped[str | None] = mapped_column(String(80), ForeignKey("goals.id"), nullable=True)
success_criteria_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class PerceptionChannel(Base):
"""烛照枢感知通道授权(契约字典 §5.7 频道级)。channel 为主键,默认六类可自定义。"""
__tablename__ = "perception_channels"
channel: Mapped[str] = mapped_column(String(80), primary_key=True)
name: Mapped[str] = mapped_column(String(120), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
consent_status: Mapped[str] = mapped_column(String(40), nullable=False, default="unknown")
privacy_level: Mapped[str] = mapped_column(String(40), nullable=False, default="normal")
risk_level: Mapped[str] = mapped_column(String(20), nullable=False, default="L2")
last_active_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class WorldEntity(Base):
"""河图枢世界实体(契约字典 §11.1 / §11.2)。"""
__tablename__ = "world_entities"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_world_entity_id)
type: Mapped[str] = mapped_column(String(40), nullable=False, default="concept", index=True)
name: Mapped[str] = mapped_column(String(200), nullable=False)
attributes_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
state_type: Mapped[str] = mapped_column(String(40), nullable=False, default="inference")
confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
sources_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class Plan(Base):
"""洛书枢计划(契约字典 §5.13)。goal_id 关联太一枢目标,可空。"""
__tablename__ = "plans"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_plan_id)
goal_id: Mapped[str | None] = mapped_column(String(80), ForeignKey("goals.id"), index=True)
title: Mapped[str] = mapped_column(String(300), nullable=False)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="draft", index=True)
assumptions_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
risks_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
required_tools_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
success_criteria_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
step_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
completed_steps: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class AgentRosterEntry(Base):
"""天工阁枢九角色在岗(契约字典 §5.14)。role 为主键,默认九角色可自定义。"""
__tablename__ = "agent_roster"
role: Mapped[str] = mapped_column(String(80), primary_key=True)
name: Mapped[str] = mapped_column(String(120), nullable=False)
responsibility: Mapped[str] = mapped_column(Text, nullable=False, default="")
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
last_active_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class GenerationPreferences(Base):
"""生成参数偏好(模型网关 §4.3)。本地单用户单行,按 owner_scope 固定。"""
__tablename__ = "generation_preferences"
owner_scope: Mapped[str] = mapped_column(
String(80), primary_key=True, default=settings.default_owner_scope
)
default_model_hint: Mapped[str] = mapped_column(String(120), nullable=False)
temperature: Mapped[float] = mapped_column(Float, nullable=False, default=0.7)
top_p: Mapped[float] = mapped_column(Float, nullable=False, default=1.0)
max_output_tokens: Mapped[int] = mapped_column(Integer, nullable=False, default=4096)
streaming: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class ToolPolicy(Base):
"""百工枢工具策略治理(api-spec §15 / §16)。
工具的风险/权限策略持久化层;``name`` 为主键。builtin 工具启动时由种子 upsert
(``source=builtin``),自定义工具经 API 注册(``source=custom``)。司命决策器
(SafetyGovernor)从本表加载覆盖策略;``/tools`` 与 ``/safety/policies`` 两组 API
共享本表——前者返回完整元数据,后者只返回策略子集。
"""
__tablename__ = "tool_policies"
name: Mapped[str] = mapped_column(String(80), primary_key=True)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
risk_level: Mapped[str] = mapped_column(String(20), nullable=False, default="L2")
permission_policy: Mapped[str] = mapped_column(String(20), nullable=False, default="ask")
side_effect: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
audit_required: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
source: Mapped[str] = mapped_column(String(20), nullable=False, default="custom")
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class ModelEndpointOverride(Base):
"""模型网关 endpoint 运行时覆盖(DB 优先于 .env / yaml,api-spec §4.4)。
每个 (logical_model_name, endpoint_name) 一行;字段非空才覆盖同名 endpoint 配置,
None = 不覆盖、回退 yaml/env。base_url/api_key 经 resolved_* 优先 endpoint 字段生效,
故 DB 写入后即「DB 优先 env」。本地单用户边界下 API key 明文存储,GET 脱敏不回值。
"""
__tablename__ = "model_endpoint_overrides"
logical_model_name: Mapped[str] = mapped_column(String(120), primary_key=True)
endpoint_name: Mapped[str] = mapped_column(String(120), primary_key=True)
base_url: Mapped[str | None] = mapped_column(String(500))
api_key: Mapped[str | None] = mapped_column(String(500))
status: Mapped[str | None] = mapped_column(String(40))
weight: Mapped[int | None] = mapped_column(Integer)
priority: Mapped[int | None] = mapped_column(Integer)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+37
View File
@@ -0,0 +1,37 @@
"""幂等键模型。"""
from datetime import datetime
from sqlalchemy import DateTime, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_id
from app.core.time import utc_now
from app.db.base import Base
def new_idempotency_id() -> str:
return new_id("idempotency_")
class IdempotencyKey(Base):
__tablename__ = "idempotency_keys"
__table_args__ = (UniqueConstraint("scope", "key", name="uq_idempotency_scope_key"),)
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_idempotency_id)
key: Mapped[str] = mapped_column(String(200), nullable=False, index=True)
scope: Mapped[str] = mapped_column(String(120), nullable=False, index=True)
method: Mapped[str] = mapped_column(String(20), nullable=False)
path: Mapped[str] = mapped_column(String(500), nullable=False)
request_hash: Mapped[str] = mapped_column(String(128), nullable=False)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="completed")
response_status_code: Mapped[int] = mapped_column(default=200)
response_body_json: Mapped[str] = mapped_column(Text, nullable=False)
resource_type: Mapped[str | None] = mapped_column(String(80))
resource_id: Mapped[str | None] = mapped_column(String(120))
trace_id: Mapped[str | None] = mapped_column(String(80), index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
+64
View File
@@ -0,0 +1,64 @@
"""人物资料导入模型(api-spec §8;契约字典 §10)。
导入流水线把用户授权的聊天记录、人物设定、背景故事、角色卡、语气样例、
语音样本等资料解析为可追溯的 ``import_items``,再由后续阶段抽取为
档案 / 记忆 / 人格候选。``import_items.id`` 作为候选的 evidence 来源
(source_type = ``import_item``)。
"""
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_import_item_id, new_import_job_id
from app.core.time import utc_now
from app.db.base import Base
class CharacterImportJob(Base):
"""导入任务(契约字典 §10.3 状态机;api-spec §8)。"""
__tablename__ = "character_import_jobs"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_import_job_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
material_type: Mapped[str] = mapped_column(String(40), nullable=False, index=True)
source_type: Mapped[str] = mapped_column(String(40), nullable=False)
title: Mapped[str | None] = mapped_column(String(200))
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
format: Mapped[str] = mapped_column(String(40), nullable=False, default="text")
media_type: Mapped[str | None] = mapped_column(String(40))
mime_type: Mapped[str | None] = mapped_column(String(80))
source_description: Mapped[str | None] = mapped_column(Text)
speaker_label: Mapped[str | None] = mapped_column(String(120))
consent_status: Mapped[str] = mapped_column(String(40), nullable=False, default="unknown")
usage_scope: Mapped[str] = mapped_column(String(40), nullable=False, default="reference_only")
checksum: Mapped[str | None] = mapped_column(String(120), index=True)
item_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
storage_path: Mapped[str | None] = mapped_column(String(500))
raw_content: Mapped[str | None] = mapped_column(Text)
error_code: Mapped[str | None] = mapped_column(String(80))
error_message: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class ImportItem(Base):
"""导入解析条目(契约字典 §10.4 item_type / §10.5 role)。"""
__tablename__ = "import_items"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_import_item_id)
import_job_id: Mapped[str] = mapped_column(
String(80), ForeignKey("character_import_jobs.id"), index=True
)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
item_type: Mapped[str] = mapped_column(String(40), nullable=False)
role: Mapped[str] = mapped_column(String(40), nullable=False, default="other")
sequence: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
content: Mapped[str] = mapped_column(Text, nullable=False)
metadata_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
+74
View File
@@ -0,0 +1,74 @@
"""世谱(Lorebook / World Info)模型。
兼容酒馆 character_book 结构 + 魂镜扩展(scope / bound_entity_id)。
纯逻辑(匹配 / 预算 / 分组)在 app.modules.lorebook,本文件仅持久化载体。
"""
from datetime import datetime
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_lorebook_entry_id, new_lorebook_id
from app.core.time import utc_now
from app.db.base import Base
class Lorebook(Base):
"""一本世谱:条目集合 + 扫描 / 预算参数。
``character_id`` 为空表示 global 作用域(跨人物共享);非空时按人物隔离。
"""
__tablename__ = "lorebooks"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_lorebook_id)
character_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("characters.id"), index=True
)
name: Mapped[str] = mapped_column(String(200), nullable=False, default="")
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
scan_depth: Mapped[int | None] = mapped_column(Integer)
token_budget: Mapped[int | None] = mapped_column(Integer)
recursive_scanning: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
scope: Mapped[str] = mapped_column(String(40), nullable=False, default="character", index=True)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="active", index=True)
owner_scope: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class LorebookEntry(Base):
"""单条世谱条目:keys 命中后注入 content。
``keys`` / ``secondary_keys`` 为字符串数组,落库存 JSON 文本(契约字典 §4)。
"""
__tablename__ = "lorebook_entries"
id: Mapped[str] = mapped_column(
String(80), primary_key=True, default=new_lorebook_entry_id
)
lorebook_id: Mapped[str] = mapped_column(
String(80), ForeignKey("lorebooks.id"), nullable=False, index=True
)
keys_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
content: Mapped[str] = mapped_column(Text, nullable=False, default="")
secondary_keys_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
constant: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
selective: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
case_sensitive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
use_regex: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
position: Mapped[str] = mapped_column(String(40), nullable=False, default="after_char")
insertion_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=10)
comment: Mapped[str] = mapped_column(String(500), nullable=False, default="")
# 软引用河图世界实体(绑定触发);不加 FK,避免跨枢强耦合
bound_entity_id: Mapped[str | None] = mapped_column(String(80), index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+43
View File
@@ -0,0 +1,43 @@
"""百工 MCP server 模型(skills-design §5)。
注册的 MCP server 元数据 + 拉取缓存的工具清单(tools_json),供启动加载与
转 ToolSpec register。
支持两种传输协议:
- streamable-http:通过 endpoint_url POST /message(标准 MCP Streamable HTTP)
- stdio:通过子进程 stdin/stdout 通信(本地 MCP 集成)
"""
from datetime import datetime
from sqlalchemy import DateTime, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_mcp_server_id
from app.core.time import utc_now
from app.db.base import Base
class McpServer(Base):
"""MCP server 注册记录。"""
__tablename__ = "mcp_servers"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_mcp_server_id)
name: Mapped[str] = mapped_column(String(120), nullable=False, unique=True)
transport: Mapped[str] = mapped_column(String(40), nullable=False, default="streamable-http")
# streamable-http 用
endpoint_url: Mapped[str | None] = mapped_column(String(500))
auth_token: Mapped[str | None] = mapped_column(String(500))
# stdio 用
command: Mapped[str | None] = mapped_column(String(500))
args_json: Mapped[str | None] = mapped_column(Text) # JSON list
env_json: Mapped[str | None] = mapped_column(Text) # JSON dict
cwd: Mapped[str | None] = mapped_column(String(500))
# 通用
status: Mapped[str] = mapped_column(String(40), nullable=False, default="enabled", index=True)
tools_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+46
View File
@@ -0,0 +1,46 @@
"""记忆模型。"""
from datetime import datetime
from sqlalchemy import DateTime, Float, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_memory_id, new_memory_link_id
from app.core.time import utc_now
from app.db.base import Base
class Memory(Base):
__tablename__ = "memories"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_memory_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
type: Mapped[str] = mapped_column(String(60), nullable=False)
title: Mapped[str] = mapped_column(String(200), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
source: Mapped[str] = mapped_column(String(80), nullable=False, default="user_explicit")
confidence: Mapped[float] = mapped_column(Float, nullable=False, default=1.0)
importance: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
sensitivity: Mapped[str] = mapped_column(String(40), nullable=False, default="low")
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
status: Mapped[str] = mapped_column(String(40), nullable=False, default="active", index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
temporal_context_json: Mapped[str] = mapped_column(
Text, nullable=False, default="{}", server_default="{}"
)
class MemoryLink(Base):
__tablename__ = "memory_links"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_memory_link_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
source_memory_id: Mapped[str] = mapped_column(String(80), ForeignKey("memories.id"), index=True)
target_memory_id: Mapped[str] = mapped_column(String(80), ForeignKey("memories.id"), index=True)
relation: Mapped[str] = mapped_column(String(80), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
+55
View File
@@ -0,0 +1,55 @@
"""人格版本模型。"""
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_personality_id, new_personality_proposal_id
from app.core.time import utc_now
from app.db.base import Base
class PersonalityVersion(Base):
__tablename__ = "personality_versions"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_personality_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
identity_profile: Mapped[str] = mapped_column(Text, nullable=False)
role_destiny: Mapped[str] = mapped_column(Text, nullable=False, default="")
personality_seed: Mapped[str] = mapped_column(Text, nullable=False, default="")
traits: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
expression_style: Mapped[str] = mapped_column(Text, nullable=False, default="")
values: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
relationship_pattern: Mapped[str] = mapped_column(Text, nullable=False, default="")
emotion_baseline: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
reason: Mapped[str] = mapped_column(Text, nullable=False, default="system_seed")
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_by: Mapped[str] = mapped_column(String(80), nullable=False, default="system")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
rolled_back_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
class PersonalityProposal(Base):
"""灵核枢人格修改建议(契约字典 §10.6 candidate.status;api-spec §11)。
提议提交后为 ``pending``,经审核批准后派生新人格版本(status=``applied``),
驳回则标记 ``rejected``;本地单用户边界下审核者即用户,写操作均记审计。
"""
__tablename__ = "personality_proposals"
id: Mapped[str] = mapped_column(
String(80), primary_key=True, default=new_personality_proposal_id
)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
fields_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
reason: Mapped[str] = mapped_column(Text, nullable=False)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
resulting_version_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("personality_versions.id")
)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
reviewed_by: Mapped[str] = mapped_column(String(80), nullable=False, default="system")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
+32
View File
@@ -0,0 +1,32 @@
"""灵核情绪状态持久化(设计 §11.4)。"""
from datetime import datetime
from sqlalchemy import DateTime, Float, ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column
from app.core.time import utc_now
from app.db.base import Base
class CharacterEmotionState(Base):
"""角色当前情绪状态(单行 per character,最新状态 + 触发溯源)。
设计 §11.4:``mood`` / ``intensity`` 由辰正 ``time.*`` 事件驱动动态维护;
``baseline_*`` 同步自人格版本 ``emotion_baseline``,作为衰减回归锚点。
同步写 ``Character.current_mood_id``(激活既有字段,前端列表无需 join)。
"""
__tablename__ = "character_emotion_states"
character_id: Mapped[str] = mapped_column(
String(80), ForeignKey("characters.id"), primary_key=True
)
mood: Mapped[str] = mapped_column(String(40), nullable=False, default="neutral")
intensity: Mapped[float] = mapped_column(Float, nullable=False, default=0.2)
baseline_mood: Mapped[str] = mapped_column(String(40), nullable=False, default="neutral")
baseline_intensity: Mapped[float] = mapped_column(Float, nullable=False, default=0.2)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
last_trigger_event_id: Mapped[str | None] = mapped_column(String(80), nullable=True)
+77
View File
@@ -0,0 +1,77 @@
"""RBAC 角色-权限模型。
Role ──╮
├── RolePermission ── Permission
User ──╯
└── UserRole ────╯
前端通过 GET /auth/me 的 permissions 字段获取当前用户权限列表,本地不做角色-权限映射。
"""
from datetime import datetime
from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_permission_id, new_role_id
from app.core.time import utc_now
from app.db.base import Base
class Role(Base):
__tablename__ = "roles"
id: Mapped[str] = mapped_column(String(40), primary_key=True, default=new_role_id)
name: Mapped[str] = mapped_column(String(80), unique=True, nullable=False, index=True)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
is_system: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class Permission(Base):
__tablename__ = "permissions"
id: Mapped[str] = mapped_column(String(40), primary_key=True, default=new_permission_id)
resource: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
action: Mapped[str] = mapped_column(String(80), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
@property
def code(self) -> str:
return f"{self.resource}:{self.action}"
class UserRole(Base):
__tablename__ = "user_roles"
user_id: Mapped[str] = mapped_column(
String(40),
ForeignKey("users.id", ondelete="CASCADE"),
primary_key=True,
)
role_id: Mapped[str] = mapped_column(
String(40),
ForeignKey("roles.id", ondelete="CASCADE"),
primary_key=True,
)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
class RolePermission(Base):
__tablename__ = "role_permissions"
role_id: Mapped[str] = mapped_column(
String(40),
ForeignKey("roles.id", ondelete="CASCADE"),
primary_key=True,
)
permission_id: Mapped[str] = mapped_column(
String(40),
ForeignKey("permissions.id", ondelete="CASCADE"),
primary_key=True,
)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
+55
View File
@@ -0,0 +1,55 @@
"""运行时 Run/Event 模型。"""
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_event_id, new_run_id
from app.core.time import utc_now
from app.db.base import Base
class Run(Base):
__tablename__ = "runs"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_run_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
session_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("chat_sessions.id"), index=True
)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="queued", index=True)
goal: Mapped[str | None] = mapped_column(String(300))
origin: Mapped[str] = mapped_column(String(40), nullable=False, default="chat")
model_hint: Mapped[str | None] = mapped_column(String(120))
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
error_code: Mapped[str | None] = mapped_column(String(80))
error_message: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class Event(Base):
__tablename__ = "events"
__table_args__ = (UniqueConstraint("run_id", "sequence", name="uq_events_run_sequence"),)
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_event_id)
character_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("characters.id"), index=True, nullable=True
)
run_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("runs.id"), index=True, nullable=True
)
type: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
source: Mapped[str] = mapped_column(String(40), nullable=False, default="system")
payload_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
visibility: Mapped[str] = mapped_column(String(40), nullable=False, default="public")
sensitivity: Mapped[str] = mapped_column(String(40), nullable=False, default="low")
trace_id: Mapped[str | None] = mapped_column(String(80), index=True)
parent_id: Mapped[str | None] = mapped_column(String(80))
sequence: Mapped[int] = mapped_column(Integer, nullable=False)
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
+54
View File
@@ -0,0 +1,54 @@
"""最小审计模型与待确认持久化记录。"""
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_audit_id
from app.core.time import utc_now
from app.db.base import Base
class AuditEvent(Base):
__tablename__ = "audit_events"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_audit_id)
pivot: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
character_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("characters.id"), index=True
)
run_id: Mapped[str | None] = mapped_column(String(80), ForeignKey("runs.id"), index=True)
action: Mapped[str] = mapped_column(String(120), nullable=False)
actor: Mapped[str] = mapped_column(String(80), nullable=False, default="system")
risk_level: Mapped[str] = mapped_column(String(20), nullable=False, default="L1")
decision: Mapped[str] = mapped_column(String(40), nullable=False, default="allow")
reason: Mapped[str | None] = mapped_column(Text)
input_summary: Mapped[str | None] = mapped_column(Text)
output_summary: Mapped[str | None] = mapped_column(Text)
trace_id: Mapped[str | None] = mapped_column(String(80), index=True)
resource_type: Mapped[str | None] = mapped_column(String(80))
resource_id: Mapped[str | None] = mapped_column(String(120))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
class PendingConfirmationRecord(Base):
"""待确认工具调用的持久化记录(多 worker 共享确认状态)。
对应 ``modules/safety/confirmation.py`` 的 ``PendingConfirmation`` 值对象。
``status`` 流转:pending → consumed / timed_out。
"""
__tablename__ = "pending_confirmations"
id: Mapped[str] = mapped_column(String(80), primary_key=True)
tool: Mapped[str] = mapped_column(String(120), nullable=False)
input_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
risk_level: Mapped[str] = mapped_column(String(40), nullable=False, default="L2")
reason: Mapped[str] = mapped_column(Text, nullable=False, default="")
run_id: Mapped[str | None] = mapped_column(String(80), index=True)
character_id: Mapped[str | None] = mapped_column(String(80), index=True)
trace_id: Mapped[str | None] = mapped_column(String(80))
status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
resolved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
+33
View File
@@ -0,0 +1,33 @@
"""百工 Skill 能力包模型(skills-design §4.1;契约字典 §10.5 skill_ 前缀)。
Skill 是显式注册的能力包:触发时注入 prompt_fragment 到对话 contract,
tool_bindings 仅作为 prompt 提示(不改变 ToolRuntime 注册集)。
"""
from datetime import datetime
from sqlalchemy import DateTime, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_skill_id
from app.core.time import utc_now
from app.db.base import Base
class Skill(Base):
"""Skill 能力包。"""
__tablename__ = "skills"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_skill_id)
name: Mapped[str] = mapped_column(String(120), nullable=False, unique=True)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
trigger: Mapped[str] = mapped_column(Text, nullable=False, default="")
prompt_fragment: Mapped[str] = mapped_column(Text, nullable=False, default="")
tool_bindings_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
risk_level: Mapped[str] = mapped_column(String(8), nullable=False, default="L1")
status: Mapped[str] = mapped_column(String(40), nullable=False, default="enabled", index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+41
View File
@@ -0,0 +1,41 @@
"""时态纪念日模型(辰正 §5.3)。"""
from datetime import datetime
from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_anniversary_id
from app.core.time import utc_now
from app.db.base import Base
class TimeAnniversary(Base):
"""纪念日 / 特殊日子(辰正设计 §5.3)。
枚举值(calendar / kind / subject_type / source)以契约字典
``docs/contracts/contract-glossary.md`` §11.4.1–§11.4.4 为单一事实源。
"""
__tablename__ = "time_anniversaries"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_anniversary_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
name: Mapped[str] = mapped_column(String(200), nullable=False)
calendar: Mapped[str] = mapped_column(String(20), nullable=False) # solar / lunar
date: Mapped[str] = mapped_column(String(20), nullable=False) # 公历 ISO 或农历日期编码
recurs_yearly: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
kind: Mapped[str] = mapped_column(
String(40), nullable=False
) # birthday / anniversary / milestone / custom
subject_type: Mapped[str] = mapped_column(
String(40), nullable=False
) # user / character_self / relationship / custom
source: Mapped[str] = mapped_column(
String(40), nullable=False, default="manual"
) # auto / manual / imported
metadata_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+25
View File
@@ -0,0 +1,25 @@
"""用户模型。"""
from datetime import datetime
from sqlalchemy import Boolean, DateTime, String
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_user_id
from app.core.time import utc_now
from app.db.base import Base
class User(Base):
__tablename__ = "users"
id: Mapped[str] = mapped_column(String(40), primary_key=True, default=new_user_id)
username: Mapped[str] = mapped_column(String(80), unique=True, nullable=False, index=True)
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
is_admin: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
+58
View File
@@ -0,0 +1,58 @@
"""河图枢世界模型运行时(api-spec §12;design/world-model-design §3/§4)。
与治理视图 ``governance.WorldEntity``(全局、人工维护、无 character_id)区分:
本模块是**按人物隔离的运行时**世界状态,所有写入绑定 ``character_id``。
自动抽取(世界引擎)属 v1 半自动留后项,本表先承载手动 CRUD 与未来抽取结果。
"""
from datetime import datetime
from sqlalchemy import DateTime, Float, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_world_entity_id, new_world_relation_id
from app.core.time import utc_now
from app.db.base import Base
class CharacterWorldEntity(Base):
"""人物视角下的世界实体(契约字典 §11.1 type / §11.2 state_type)。"""
__tablename__ = "character_world_entities"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_world_entity_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
type: Mapped[str] = mapped_column(String(40), nullable=False, default="concept", index=True)
name: Mapped[str] = mapped_column(String(200), nullable=False)
attributes_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
state_type: Mapped[str] = mapped_column(String(40), nullable=False, default="inference")
status: Mapped[str] = mapped_column(String(40), nullable=False, default="confirmed", index=True)
confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
sources_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class WorldRelation(Base):
"""实体间关系(design §4;predicate 为自由字符串谓词,非枚举)。"""
__tablename__ = "world_relations"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_world_relation_id)
character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True)
subject_id: Mapped[str] = mapped_column(
String(80), ForeignKey("character_world_entities.id"), index=True
)
predicate: Mapped[str] = mapped_column(String(120), nullable=False)
object_id: Mapped[str] = mapped_column(
String(80), ForeignKey("character_world_entities.id"), index=True
)
confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.5)
evidence_ids_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)