"""角色卡导出映射:魂镜 Character + 世谱 → ST V3 卡静态快照。 反向于 import mapping。导出为静态快照——丢弃演化维度(人格版本 / 记忆 / 情绪), 仅保留当前设定 + 世谱。``mission`` / ``safety_guidelines`` 道体无 ST 对应字段,不导出。 """ from __future__ import annotations import json from app.models.character import Character from app.models.lorebook import Lorebook, LorebookEntry def character_to_card( character: Character, lorebook: Lorebook | None = None, entries: list[LorebookEntry] | None = None, ) -> dict: """Character + 世谱 → ST V3 卡 dict。""" profile = json.loads(character.profile_json or "{}") source_meta = profile.get("source_meta") or {} data: dict = { "name": character.name, "description": profile.get("background", ""), "personality": character.personality_summary or "", "scenario": profile.get("scenario", ""), "first_mes": profile.get("first_mes", ""), "mes_example": profile.get("mes_example", ""), "alternate_greetings": profile.get("alternate_greetings", []), "tags": profile.get("tags", []), "creator": source_meta.get("creator", ""), "character_version": source_meta.get("character_version", ""), "creator_notes": source_meta.get("creator_notes", ""), "system_prompt": profile.get("system_prompt", ""), "post_history_instructions": profile.get("post_history_instructions", ""), "extensions": profile.get("st_extensions", {}), } if character.display_name and character.display_name != character.name: data["nickname"] = character.display_name if lorebook is not None: data["character_book"] = lorebook_to_st_book(lorebook, entries or []) return {"spec": "chara_card_v3", "spec_version": "3.0", "data": data} def lorebook_to_st_book(lorebook: Lorebook, entries: list[LorebookEntry]) -> dict: return { "name": lorebook.name, "description": lorebook.description, "scan_depth": lorebook.scan_depth, "token_budget": lorebook.token_budget, "recursive_scanning": lorebook.recursive_scanning, "entries": [entry_to_st(e) for e in entries], } def entry_to_st(entry: LorebookEntry) -> dict: return { "keys": json.loads(entry.keys_json or "[]"), "content": entry.content, "secondary_keys": json.loads(entry.secondary_keys_json or "[]"), "constant": entry.constant, "selective": entry.selective, "enabled": entry.enabled, "case_sensitive": entry.case_sensitive, "use_regex": entry.use_regex, "position": entry.position, "insertion_order": entry.insertion_order, "priority": entry.priority, "comment": entry.comment, }