feat(orchestrator): 续写链前一章无已验收终稿发 warning——静默丢前文引子可观测(CR-L1)
This commit is contained in:
@@ -13,6 +13,7 @@ from collections.abc import AsyncIterator
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
import structlog
|
||||||
from langchain_core.runnables import RunnableConfig
|
from langchain_core.runnables import RunnableConfig
|
||||||
from langgraph.checkpoint.memory import MemorySaver
|
from langgraph.checkpoint.memory import MemorySaver
|
||||||
from langgraph.types import Command
|
from langgraph.types import Command
|
||||||
@@ -446,6 +447,57 @@ async def test_write_chapter_continue_volume_injects_prior_accepted_text() -> No
|
|||||||
assert "前文正文" in write_input # build_continuation_context 的小节标题
|
assert "前文正文" in write_input # build_continuation_context 的小节标题
|
||||||
|
|
||||||
|
|
||||||
|
async def test_continue_volume_warns_when_prior_chapter_unaccepted() -> None:
|
||||||
|
"""continue_volume 写第 2 章但第 1 章无已验收终稿 → 静默丢前文引子须发 warning(CR-L1)。"""
|
||||||
|
h = _make_harness(conflicts=[])
|
||||||
|
# accepted_store 故意留空:第 1 章尚无已验收正文 → 续写丢失前文引子(静默降级)。
|
||||||
|
state = initial_chain_state(
|
||||||
|
project_id=PROJECT,
|
||||||
|
user_id=USER,
|
||||||
|
start_chapter_no=2,
|
||||||
|
count=1,
|
||||||
|
chain_key="continue_volume",
|
||||||
|
)
|
||||||
|
|
||||||
|
with structlog.testing.capture_logs() as logs:
|
||||||
|
await write_chapter(
|
||||||
|
state,
|
||||||
|
gateway_builder=h["gateway_builder"],
|
||||||
|
session_factory=h["session_factory"],
|
||||||
|
memory_repos_factory=h["memory_repos_factory"],
|
||||||
|
chapter_repo_factory=h["chapter_repo_factory"],
|
||||||
|
assemble=h["assemble"],
|
||||||
|
)
|
||||||
|
|
||||||
|
warning = next(e for e in logs if e["event"] == "chain_prior_accepted_missing")
|
||||||
|
assert warning["log_level"] == "warning"
|
||||||
|
assert warning["chapter_no"] == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_continue_volume_first_chapter_does_not_warn() -> None:
|
||||||
|
"""第 1 章前一章号 < 1,无前文属预期(非降级)→ 不应发缺前文 warning。"""
|
||||||
|
h = _make_harness(conflicts=[])
|
||||||
|
state = initial_chain_state(
|
||||||
|
project_id=PROJECT,
|
||||||
|
user_id=USER,
|
||||||
|
start_chapter_no=1,
|
||||||
|
count=1,
|
||||||
|
chain_key="continue_volume",
|
||||||
|
)
|
||||||
|
|
||||||
|
with structlog.testing.capture_logs() as logs:
|
||||||
|
await write_chapter(
|
||||||
|
state,
|
||||||
|
gateway_builder=h["gateway_builder"],
|
||||||
|
session_factory=h["session_factory"],
|
||||||
|
memory_repos_factory=h["memory_repos_factory"],
|
||||||
|
chapter_repo_factory=h["chapter_repo_factory"],
|
||||||
|
assemble=h["assemble"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not [e for e in logs if e["event"] == "chain_prior_accepted_missing"]
|
||||||
|
|
||||||
|
|
||||||
async def test_continue_volume_chain_second_chapter_sees_first_chapter_text() -> None:
|
async def test_continue_volume_chain_second_chapter_sees_first_chapter_text() -> None:
|
||||||
"""全图 E2E:continue_volume 两章——第 2 章请求上下文含第 1 章正文(不变量 #1/#5)。"""
|
"""全图 E2E:continue_volume 两章——第 2 章请求上下文含第 1 章正文(不变量 #1/#5)。"""
|
||||||
h = _make_harness(conflicts=[])
|
h = _make_harness(conflicts=[])
|
||||||
|
|||||||
@@ -325,7 +325,17 @@ async def _read_prior_accepted(
|
|||||||
if chapter_no <= 1:
|
if chapter_no <= 1:
|
||||||
return ""
|
return ""
|
||||||
view = await chapter_repo.latest_accepted(project_id, chapter_no - 1)
|
view = await chapter_repo.latest_accepted(project_id, chapter_no - 1)
|
||||||
return view.content if view is not None else ""
|
if view is None:
|
||||||
|
# 静默降级须可观测(CR-L1):续写式链本章续接的前一章尚无已验收终稿——续写会丢失
|
||||||
|
# 前文引子(降级为 build_continuation_context 的占位)。发 warning 便于查错,勿静默吞。
|
||||||
|
log.warning(
|
||||||
|
"chain_prior_accepted_missing",
|
||||||
|
project_id=str(project_id),
|
||||||
|
chapter_no=chapter_no,
|
||||||
|
prior_chapter_no=chapter_no - 1,
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
return view.content
|
||||||
|
|
||||||
|
|
||||||
async def _commit(session: CommitSession) -> None:
|
async def _commit(session: CommitSession) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user