"""角色卡导入 / 导出。 rewrite/1.0 M2b:导入不再直接建人物——ST V1/V2/V3 卡解析后产出**可审阅灵胚** (产品 PRD §5.2「导入默认生成可审阅灵胚」);``character_book`` 暂存于灵胚道体草稿, 启镜时物化为新人物的世谱书与条目(见 jingsheng.service.activate_embryo)。 导出方向保持:人物 → ST V3 卡静态快照。 """ from __future__ import annotations from app.core.errors import AppError from app.db.session import AsyncSessionLocal from app.models.character import Character from app.modules.imports.card_parser import parse_character_card from app.modules.imports.export_mapping import character_to_card def card_to_embryo_payload(raw: bytes) -> dict: """解析角色卡字节 → 灵胚创建载荷(name/description/identity_baseline)。 identity_baseline 保存卡的全部有效字段与原始 character_book, 启镜前可经照心编辑(PATCH /embryo-drafts/{id})审阅修改。 """ card = parse_character_card(raw) description = "\n\n".join( part for part in ( card.description.strip(), f"【性格】{card.personality.strip()}" if card.personality.strip() else "", f"【场景】{card.scenario.strip()}" if card.scenario.strip() else "", ) if part ) baseline = { "import_card": { "spec_version": card.spec_version, "description": card.description, "personality": card.personality, "scenario": card.scenario, "first_mes": card.first_mes, "mes_example": card.mes_example, "system_prompt": card.system_prompt, "post_history_instructions": card.post_history_instructions, "alternate_greetings": card.alternate_greetings, "tags": card.tags, "creator": card.creator, "character_version": card.character_version, "character_book": card.character_book, } } name = card.name.strip() or "未命名灵胚" return { "name": name[:120], "description": description or None, "identity_baseline": baseline, "book_entry_count": _count_book_entries(card.character_book), } def _count_book_entries(book: dict | None) -> int: if not book: return 0 entries = book.get("entries") if isinstance(entries, (dict, list)): return len(entries) return 0 async def export_character_card(character_id: str, owner_scope: str) -> dict: """导出人物为 ST V3 卡静态快照(丢弃演化维度)。""" async with AsyncSessionLocal() as session: character = await session.get(Character, character_id) if character is None or character.owner_scope != owner_scope: raise AppError("not_found", "人物不存在。") return character_to_card(character)