"""创作工具箱 · context 派发(PURE,LLM-free,确定性)。 把 `ContextStrategy` 映射成喂给网关的注入文本。**纯函数**:给定已组装好的材料 (项目设定 / 世界观文本 / 本章节拍),同输入同输出、无 IO、无时间戳——便于单测与缓存。 IO(读 projects/world_entities/outline)发生在端点;本模块只负责「材料 → 注入文本」的 确定性拼装,复用 `ww_core.orchestrator` 的既有 context builders(brief / outline-chapter) 与 `routers/generation` 的 `_project_context` / `_world_context` 序列化口径。 策略 → 材料: - `brief_only` :作品设定(仅标题/题材级)+ 一句话需求; - `with_project` :作品设定(含前提/主题)+ 一句话需求; - `with_world` :作品设定 + world_entities 硬规则卡 + 一句话需求; - `with_outline_chapter`:作品设定 + 指定章大纲节拍(缺章/缺大纲 → 空节拍,不报错); - `with_prior_chapter` :作品设定 + 最新已写正文(端点先读 accepted/draft)+ 可选节拍; - `text_input` :作品设定 + 作者提供的原文(扩写/降AI/拆书样本)+ 一句话需求。 """ from __future__ import annotations from collections.abc import Sequence from ww_core.orchestrator import ( build_brief_context, build_continuation_context, build_outline_chapter_context, build_text_input_context, ) from ww_skills import ContextStrategy def build_toolbox_context( strategy: ContextStrategy, *, brief: str, project_context: str, world_context: str = "", chapter_no: int | None = None, beats: Sequence[str] = (), prior_text: str = "", text: str = "", ) -> str: """据注入策略组装喂网关的文本(PURE,确定性)。 `project_context` 由调用方按策略序列化(brief_only 给精简设定、with_project 给完整设定); `world_context` 仅 with_world 用(已序列化的硬规则卡);`chapter_no`/`beats` 仅 with_outline_chapter 用(缺章/缺大纲时 beats 为空,降级到空节拍而非报错); `prior_text` 仅 with_prior_chapter 用(端点先读最新 accepted/draft 正文注入); `text` 仅 text_input 用(作者提供的原文样本)。 """ if strategy in ("brief_only", "with_project"): return build_brief_context(brief=brief, project_context=project_context) if strategy == "with_prior_chapter": return build_continuation_context( project_context=project_context, prior_text=prior_text, beats=list(beats) if beats else None, ) if strategy == "text_input": return build_text_input_context(project_context=project_context, text=text, brief=brief) if strategy == "with_world": base = build_brief_context(brief=brief, project_context=project_context) world_block = world_context.strip() or "(暂无世界观设定)" return f"{base}\n\n## 世界观(硬规则供契合/校验)\n{world_block}" if strategy == "with_outline_chapter": # 缺章号时退回首章占位(端点对需要章号的工具会先校验,这里只做确定性兜底)。 no = chapter_no if chapter_no is not None else 1 outline_block = build_outline_chapter_context( chapter_no=no, beats=beats, project_context=project_context ) brief_block = brief.strip() if brief_block: return f"{outline_block}\n\n## 创作需求\n{brief_block}" return outline_block # 受限枚举不应到达此分支;保守抛错而非静默错配策略。 raise ValueError(f"unknown context strategy: {strategy!r}")