M6:缘契、人格与世界——契约版本链、铸魂分灵、河图境界与三界分流
- 缘契域 domains/yuanqi:covenants(当前版)+ covenant_events(不可变历史);提议—问心—铭契—撤回全链;边界变更立即生效;多维关系并存,服务层拒绝 affinity/好感度单一键;浪漫维度成人资格标记位 - 道体铸魂与分灵 domains/jingsheng/soul.py:soul_forgings 确认流(锚点变更 pending→confirmed 应用并签发新魂籍版本,确认前人物锚点不变);分灵创建新人物(继承资料与谱系 lineage 链、不继承原身份,魂籍从 v1 起) - 河图域 domains/hetu:persistent_worlds 境界 + world_entities/world_relations 境界作用域重建 + 世谱挂接;境界正史视图(scope=persistent_world 归真集合);canonize 后置推进正史计数 - 三界分流:reality→息壤提取(不变);persistent_world→境界实录+正史推进;simulation→只记试演实录;factual_records 增 scope 列——境界/幻境实录与真界本相隔离 - 正影打通:引文 target_type=memory 时被引记忆 superseded → 返回 successor_memory_id 提示,不静默替换 - 前端:人物详情页缘契面板(提议维度选择/铭契/撤回/事件史)+ 世界页重建(境界目录/实体/正史概览)+ covenants/worlds api 库 - 退役:旧 personality/drive/evolution/world 模块、服务、端点、仓储、schema、前端五页与组件样式;迁移 007 退役九张旧表与 characters.current_personality_version_id - 验收(test_m6_domains 六用例):多维无单一好感度/铭契版本链/事件不可抹除/撤回立即生效/边界即时/铸魂确认流+魂籍版本/分灵谱系/境界正史不进真界(实录 scope 隔离+真界候选=0)/引文正影后继 - 四套件:后端 379、前端 199、BFF 4、神识 5 包全绿(2 个预存环境失败不变);迁移 007 净库+±1 干净
This commit is contained in:
1 parent
684261f4b4
commit
7e23d6fa84
108 files changed
+2600
-9732
No files matched your search
@@ -9,7 +9,6 @@ from app.core.errors import AppError
|
||||
from app.core.time import utc_now
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.character import Character
|
||||
from app.models.personality import PersonalityVersion
|
||||
from app.modules.character import (
|
||||
MODEL_POLICY_CREATE_DEFAULTS,
|
||||
SYSTEM_TEMPLATE_OWNER_SCOPE,
|
||||
@@ -17,7 +16,6 @@ from app.modules.character import (
|
||||
sanitize_profile,
|
||||
)
|
||||
from app.repositories import characters as character_repo
|
||||
from app.repositories import personality as personality_repo
|
||||
from app.repositories.json_utils import dumps, loads_dict
|
||||
from app.schemas.character import (
|
||||
ActivateCharacterResponse,
|
||||
@@ -100,25 +98,6 @@ async def create_default_character(
|
||||
return await create_character_from_template(session, template, owner_scope)
|
||||
|
||||
|
||||
async def ensure_character_personality(
|
||||
session: AsyncSession, character: Character
|
||||
) -> PersonalityVersion:
|
||||
from app.services.personality import create_personality_from_profile
|
||||
|
||||
personality = None
|
||||
if character.current_personality_version_id:
|
||||
personality = await personality_repo.get_personality(
|
||||
session, character.current_personality_version_id
|
||||
)
|
||||
if personality is None:
|
||||
personality = await personality_repo.get_latest_personality(session, character.id)
|
||||
if personality is None:
|
||||
personality = await create_personality_from_profile(session, character)
|
||||
if character.current_personality_version_id != personality.id:
|
||||
character.current_personality_version_id = personality.id
|
||||
await session.flush()
|
||||
return personality
|
||||
|
||||
|
||||
async def ensure_default_system_template(session: AsyncSession) -> Character:
|
||||
"""按 config/character_templates.yaml 确保系统模板存在且最新。
|
||||
@@ -135,7 +114,6 @@ async def ensure_default_system_template(session: AsyncSession) -> Character:
|
||||
# create 路径已由 _create_template_from_config 兜底 birthday,
|
||||
# sync 路径可能因旧数据缺失 birthday,再兜一次确保存在。
|
||||
await _ensure_default_birthday(session, template)
|
||||
await ensure_character_personality(session, template)
|
||||
return template
|
||||
|
||||
|
||||
@@ -160,7 +138,6 @@ async def ensure_all_system_templates(session: AsyncSession) -> list[Character]:
|
||||
else:
|
||||
await _sync_template_from_config(session, template, config)
|
||||
await _ensure_default_birthday(session, template)
|
||||
await ensure_character_personality(session, template)
|
||||
results.append(template)
|
||||
|
||||
return results
|
||||
@@ -267,7 +244,6 @@ async def ensure_default_character_for_owner(session: AsyncSession, owner_scope:
|
||||
if not configs:
|
||||
template = await ensure_default_system_template(session)
|
||||
character = await create_character_from_template(session, template, owner_scope)
|
||||
await _ensure_character_personality_from_template(session, template, character)
|
||||
return character
|
||||
|
||||
# 先确保所有系统模板在 DB 中存在
|
||||
@@ -278,7 +254,6 @@ async def ensure_default_character_for_owner(session: AsyncSession, owner_scope:
|
||||
config = configs[key]
|
||||
tpl = await _create_template_from_config(session, config)
|
||||
await _ensure_default_birthday(session, tpl)
|
||||
await ensure_character_personality(session, tpl)
|
||||
templates[key] = tpl
|
||||
|
||||
# 筛选 is_default=true 的配置 key
|
||||
@@ -295,19 +270,11 @@ async def ensure_default_character_for_owner(session: AsyncSession, owner_scope:
|
||||
session, tpl.id, owner_scope
|
||||
)
|
||||
if existing is not None:
|
||||
await ensure_character_personality(session, existing)
|
||||
if primary is None:
|
||||
primary = existing
|
||||
continue
|
||||
|
||||
character = await create_character_from_template(session, tpl, owner_scope)
|
||||
from app.services.personality import copy_personality_to_character
|
||||
|
||||
template_personality = await ensure_character_personality(session, tpl)
|
||||
personality = await copy_personality_to_character(
|
||||
session, template_personality, character.id
|
||||
)
|
||||
character.current_personality_version_id = personality.id
|
||||
await session.flush()
|
||||
|
||||
await record_audit_event(
|
||||
@@ -327,32 +294,11 @@ async def ensure_default_character_for_owner(session: AsyncSession, owner_scope:
|
||||
if primary is None:
|
||||
template = await ensure_default_system_template(session)
|
||||
primary = await create_character_from_template(session, template, owner_scope)
|
||||
template_personality = await ensure_character_personality(session, template)
|
||||
from app.services.personality import copy_personality_to_character
|
||||
|
||||
personality = await copy_personality_to_character(session, template_personality, primary.id)
|
||||
primary.current_personality_version_id = personality.id
|
||||
await session.flush()
|
||||
|
||||
return primary
|
||||
|
||||
|
||||
async def _ensure_character_personality_from_template(
|
||||
session: AsyncSession,
|
||||
template: Character,
|
||||
character: Character,
|
||||
) -> None:
|
||||
"""从模板复制人格到目标人物。"""
|
||||
from app.services.personality import copy_personality_to_character
|
||||
|
||||
template_personality = await ensure_character_personality(session, template)
|
||||
personality = await copy_personality_to_character(session, template_personality, character.id)
|
||||
character.current_personality_version_id = personality.id
|
||||
await session.flush()
|
||||
|
||||
|
||||
# —— 人物管理 API(api-spec §6)——
|
||||
|
||||
|
||||
async def get_owned_character(
|
||||
session: AsyncSession, character_id: str, owner_scope: str = settings.default_owner_scope
|
||||
@@ -364,12 +310,10 @@ async def get_owned_character(
|
||||
return character
|
||||
|
||||
|
||||
async def _personality_version_number(session: AsyncSession, character: Character) -> int:
|
||||
version_id = character.current_personality_version_id
|
||||
if not version_id:
|
||||
return 0
|
||||
personality = await personality_repo.get_personality(session, version_id)
|
||||
return personality.version if personality else 0
|
||||
def _personality_version_number(character: Character) -> int:
|
||||
"""人格版本随旧版本表退役——版本语义由魂籍(identity_record)承载,此处恒 0 占位。"""
|
||||
_ = character
|
||||
return 0
|
||||
|
||||
|
||||
async def _audit_character(session: AsyncSession, character: Character, action: str) -> None:
|
||||
@@ -435,7 +379,6 @@ async def create_character(
|
||||
)
|
||||
await character_repo.add_character(session, character)
|
||||
await _ensure_default_birthday(session, character)
|
||||
await ensure_character_personality(session, character)
|
||||
await _audit_character(session, character, "character.created_from_template")
|
||||
await session.commit()
|
||||
await session.refresh(character)
|
||||
@@ -488,7 +431,7 @@ async def list_characters_page(
|
||||
character_to_summary(
|
||||
character,
|
||||
memory_count=await character_repo.count_memories(session, character.id),
|
||||
personality_version=await _personality_version_number(session, character),
|
||||
personality_version=_personality_version_number(character),
|
||||
)
|
||||
for character in items
|
||||
]
|
||||
@@ -569,7 +512,6 @@ async def activate_character(
|
||||
await _deactivate_others(session, character, owner_scope)
|
||||
character.status = "active"
|
||||
character.activated_at = utc_now()
|
||||
await ensure_character_personality(session, character)
|
||||
await _audit_character(session, character, "character.activated")
|
||||
await session.commit()
|
||||
return ActivateCharacterResponse(active_character_id=character.id, status="active")
|
||||
@@ -599,7 +541,6 @@ async def resume_character(
|
||||
await _deactivate_others(session, character, owner_scope)
|
||||
character.status = "active"
|
||||
character.activated_at = utc_now()
|
||||
await ensure_character_personality(session, character)
|
||||
await _audit_character(session, character, "character.resumed")
|
||||
await session.commit()
|
||||
await session.refresh(character)
|
||||
|
||||
Reference in new issue
Block a user