续写/扩写/降AI率/拆书 4 个生成器的声明(preview-only writes=[],只声明 tier #2): - schemas.py: ContinuationResult/PolishResult/DeAiResult/BookTeardownResult - specs.py: continue(writer)/expand(writer)/de-ai(analyst)/teardown(analyst),各配 system_prompt - generation_node.py: build_continuation_context(续写读前文)/build_text_input_context(原文输入) - 单测: 4 spec/schema 契约 + 2 builder 纯函数(确定性/空值降级) 守不变量 #2(只声明 tier)/#3(纯预览不写库)/#9(system_prompt 进缓存前缀)。
90 lines
3.0 KiB
Python
90 lines
3.0 KiB
Python
"""Scope B 竞品快赢 · 4 个生成器的 spec/schema 契约测试。
|
||
|
||
校验每个 spec 的 name/tier/reads/writes/output_schema/scope/input_schema,
|
||
对齐不变量 #2(只声明 tier)/ #3(4 生成器一律纯预览 writes=[])。不联网、无 DB。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
from ww_agents import (
|
||
AgentSpec,
|
||
BookTeardownResult,
|
||
ContinuationResult,
|
||
DeAiResult,
|
||
PolishResult,
|
||
continue_spec,
|
||
de_ai_spec,
|
||
expand_spec,
|
||
teardown_spec,
|
||
)
|
||
|
||
# (spec, name, tier, reads, output_schema)
|
||
_COMPETITOR_SPECS = [
|
||
(continue_spec, "continue", "writer", ["projects", "outline", "chapters"], ContinuationResult),
|
||
(expand_spec, "expand", "writer", ["projects"], PolishResult),
|
||
(de_ai_spec, "de-ai", "analyst", ["projects"], DeAiResult),
|
||
(teardown_spec, "teardown", "analyst", ["projects"], BookTeardownResult),
|
||
]
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("spec", "name", "tier", "reads", "output_schema"),
|
||
_COMPETITOR_SPECS,
|
||
)
|
||
def test_competitor_spec_declares_expected_contract(
|
||
spec: AgentSpec,
|
||
name: str,
|
||
tier: str,
|
||
reads: list[str],
|
||
output_schema: type,
|
||
) -> None:
|
||
assert spec.name == name
|
||
assert spec.tier == tier
|
||
assert spec.reads == reads
|
||
assert spec.writes == [] # 不变量 #3:4 生成器一律纯预览
|
||
assert spec.output_schema is output_schema
|
||
assert spec.scope == "builtin"
|
||
assert spec.input_schema is None # 注入材料为序列化文本,非结构化入参
|
||
|
||
|
||
@pytest.mark.parametrize("spec", [row[0] for row in _COMPETITOR_SPECS])
|
||
def test_competitor_spec_has_nonempty_system_prompt(spec: AgentSpec) -> None:
|
||
assert spec.system_prompt.strip()
|
||
|
||
|
||
# ---- schema 解析/默认值契约 ----
|
||
|
||
|
||
def test_continuation_result_parses_text() -> None:
|
||
parsed = ContinuationResult.model_validate({"text": "雷光再起,他迈步向前。"})
|
||
assert parsed.text == "雷光再起,他迈步向前。"
|
||
|
||
|
||
def test_polish_result_parses_text() -> None:
|
||
parsed = PolishResult.model_validate({"text": "夜色更浓,风穿过断壁。"})
|
||
assert parsed.text == "夜色更浓,风穿过断壁。"
|
||
|
||
|
||
def test_de_ai_result_parses_text() -> None:
|
||
parsed = DeAiResult.model_validate({"text": "他咧嘴一笑,没多说什么。"})
|
||
assert parsed.text == "他咧嘴一笑,没多说什么。"
|
||
|
||
|
||
def test_book_teardown_result_parses_and_lists_default_empty() -> None:
|
||
empty = BookTeardownResult(structure="开篇立钩→铺垫→爆发")
|
||
assert empty.themes == []
|
||
assert empty.archetypes == []
|
||
assert empty.hooks == []
|
||
parsed = BookTeardownResult.model_validate(
|
||
{
|
||
"themes": ["逆袭", "复仇"],
|
||
"archetypes": ["废柴主角", "腹黑反派"],
|
||
"structure": "黄金三章立爽点,逐卷升级",
|
||
"hooks": ["章末反转", "扮猪吃虎"],
|
||
}
|
||
)
|
||
assert parsed.themes == ["逆袭", "复仇"]
|
||
assert parsed.structure == "黄金三章立爽点,逐卷升级"
|
||
assert parsed.hooks == ["章末反转", "扮猪吃虎"]
|