Files
writer-work-flow/apps/api/ww_api/services/toolbox_context.py
Yaojia Wang a5351320a2 feat(skills): Scope B A2 — 4 竞品生成器注册表+上下文分派+端点接线
注册表 TOOLBOX +4 entry(continue/expand/de-ai/teardown,全 preview-only writes=[]);
ContextStrategy +with_prior_chapter/text_input;ToolGenerateRequest +text 字段。
toolbox_context 分派新策略(续写→build_continuation_context、原文→build_text_input_context);
路由续写经 chapter_repo 读最新 accepted/draft 正文注入 builder(core 不 import apps/api,仿 accept_op)。
预览仅 commit ledger 不写业务表(#3);只声明 tier(#2);system_prompt 进缓存前缀(#9)。
单测:4 生成器 generate 预览 + 续写读前文/草稿路由 + 扩写/降AI text 路由 + 拆书结构化;
更新 registry/strategy 计数测试(15 工具 / 6 策略)。门禁绿:ruff/format/mypy 196 files/pytest 168(api)+32(skills)。
2026-06-23 19:10:35 +02:00

82 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""创作工具箱 · context 派发PURELLM-free确定性
把 `ContextStrategy` 映射成喂给网关的注入文本。**纯函数**:给定已组装好的材料
(项目设定 / 世界观文本 / 本章节拍),同输入同输出、无 IO、无时间戳——便于单测与缓存。
IO读 projects/world_entities/outline发生在端点本模块只负责「材料 → 注入文本」的
确定性拼装,复用 `ww_core.orchestrator` 的既有 context buildersbrief / 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}")