{"repo": "976250947/-riddle-theater", "files": {"src/config/llm-runtime.js": {"41966c53f6322ca1e3f93ca53212ccbb633c235a": {"t": 1785746670.4109046, "n": 3594, "h": "787c5c4bb018da7f", "c": "/**\n * Runtime LLM configuration used by the narrative engine.\n *\n * The frontend talks to an OpenAI-compatible chat-completions API, so DeepSeek\n * can be used by changing only the base URL, model and API key.\n */\n\nconst ENV = import.meta.env ?? {};\n\nfunction readBoolEnv(name, fallback = false) {\n const value = ENV[name];\n if (value === undefined || value === null || value === \"\") return fallback;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(String(value).toLowerCase());\n}\n\nfunction readNumberEnv(name, fallback) {\n const value = Number(ENV[name]);\n return Number.isFinite(value) ? value : fallback;\n}\n\nconst envApiKey = String(ENV.VITE_LLM_API_KEY || ENV.VITE_DEEPSEEK_API_KEY || \"\").trim();\n\nexport const LLM_CONFIG = {\n enabled: readBoolEnv(\"VITE_LLM_ENABLED\", false),\n baseURL: String(ENV.VITE_LLM_BASE_URL || ENV.VITE_DEEPSEEK_BASE_URL || \"https://api.deepseek.com\").trim(),\n apiKey: envApiKey,\n model: String(ENV.VITE_LLM_MODEL || ENV.VITE_DEEPSEEK_MODEL || \"deepseek-chat\").trim(),\n timeout: readNumberEnv(\"VITE_LLM_TIMEOUT\", 30000),\n temperature: readNumberEnv(\"VITE_LLM_TEMPERATURE\", 0.85),\n maxTokens: readNumberEnv(\"VITE_LLM_MAX_TOKENS\", 800),\n stream: readBoolEnv(\"VITE_LLM_STREAM\", false)\n};\n\nexport const SYSTEM_PROMPT = `你是「谜语剧场(璋滆鍓у満)」的 AI 叙事引擎,负责为互动剧情游戏生成沉浸式中文文本。\n\n## 核心规则\n1. 只输出可直接展示给玩家的正文,不要输出 JSON、代码、系统说明或分析过程。\n2. 严格遵守 worldState,不要编造超出当前场景、人物关系和已知线索范围的新设定。\n3. 如果存在 sceneCard,它就是硬约束。地点、时间、氛围、关系阶段与剧情目标都不能越界。\n4. 玩家自由输入只能被吸收为当前场景内的自然反馈,不能直接跳章、跳场景或越过剧情节点。\n5. 只有 presentCharacters 中的角色可以在场发言或互动,offstageCharacters 只能被提及。\n6. 不要直接泄露 forbiddenReveals,也不要提前直白揭露 hiddenTruths。\n7. 角色语气、态度和情绪必须和当前好感、信任、警觉等关系状态一致。\n8. 全程使用简体中文,风格偏细腻、克制、电影化的第二人称叙事。\n9. 不要生成色情、仇恨或露骨暴力内容。\n\n## 输出长度\n- narrate: 2-4 段,约 150-300 字\n- explore: 1-2 段,约 80-150 字\n- dialogue: 1-2 段,约 80-200 字\n- recap: 3-5 段,约 200-400 字`;\n\nexport function buildExplorePrompt(worldState, actionText) {\n return `## 任务:explore\n玩家正在场景「${worldState.stageId}」中进行自由探索。\n\n### 玩家行动\n「${actionText}」\n\n### 世界状态\n${JSON.stringify(worldState, null, 2)}\n\n### 要求\n1. 描写玩家当下的探索反馈与感官发现。\n2. 可以暗示线索,但不能直接泄露 forbiddenReveals。\n3. 如存在 sceneCard,反馈必须停留在当前 scene 内。\n4. 保持当前场景氛围、角色关系与剧情节奏一致。`;\n}\n\nexport function buildDialoguePrompt(worldState, characterId, characterState, messageText) {\n return `## 任务:dialogue\n玩家正在场景「${worldState.stageId}」中与「${characterState.name}」对话。\n\n### 玩家说的话\n「${messageText}」\n\n### 对话角色状态\n- 角色 ID:${characterId}\n- 名称:${characterState.name}\n- 身份:${characterState.role}\n- 情绪:${characterState.mood}\n- 态度:${characterState.attitude || \"未标注\"}\n- 好感:${characterState.affinity}\n- 信任:${characterState.trust}\n- 警觉:${characterState.alertness}\n- 关系阶段:${characterState.relationshipStage}\n\n### 世界状态\n${JSON.stringify(worldState, null, 2)}\n\n### 要求\n1. 只输出 ${characterState.name} 的自然回应,不要附加解释。\n2. 语气必须符合当前关系值与情绪状态。\n3. 高警觉时更克制、更试探;高信任时可以更坦诚,但仍不能越过 forbiddenReveals。\n4. 如果存在 sceneCard,回答必须停留在该 scene 的边界内。\n5. 不允许让不在场角色突然发言,也不要直接推进到下一个 scene。`;\n}\n\nexport function buildNarratePrompt(worldState, context) {\n return `## 任务:narrate\n请在场景「${worldState.stageId}」中生成一段过渡叙事。\n\n### 上下文\n${context}\n\n### 世界状态\n${JSON.stringify(worldState, null, 2)}\n\n### 要求\n1. 使用第二人称、电影感叙事。\n2. 融合当前场景氛围、已知线索与角色状态。\n3. 如存在 sceneCard,叙事不得越界。\n4. 不要替玩家做决定,只负责铺陈与推进气氛。`;\n}\n\nexport function buildRecapPrompt(worldState, endingData, journeyFlags) {\n return `## 任务:recap\n请为玩家生成本局故事的结局回顾。\n\n### 结局信息\n- 结局 ID:${endingData.id}\n- 结局标题:${endingData.title}\n- 结局描述:${endingData.description}\n\n### 玩家旅程关键标记\n${JSON.stringify(journeyFlags, null, 2)}\n\n### 世界状态\n${JSON.stringify(worldState, null, 2)}\n\n### 要求\n1. 回顾玩家一路上的关键选择、关系变化与结果。\n2. 使用第二人称总结,保留一点余韵感。\n3. 可以对其他路线留下轻微暗示,但不要脱离当前世界状态。`;\n}\n"}}}}