Files
life-echo/api/tests/test_memoir_two_phase.py
Kevin 59d4b19d7d feat(api): 回忆录管线简化、路由延迟池与相关加固
- Phase1/2:移除 MemoirOrchestrator.run 与 process_memoir_segments 别名;文档改为 process_memoir_phase1。
- 槽位校验集中到 stage_constants(filter_stage_slots),批处理与顺序路径及 state_service 写库一致。
- StoryRoute:no_llm/parse_error/invalid_target 保守 new_story;短篇护栏不覆盖这些 fallback。
- Phase2 低置信单路径可选延迟(StoryPipelineResult.deferred):不写 Chapter/Story,Segment 记录 defer 元数据,冷却内不重复消费;上限后停自动重试,Phase1 同类目新段唤醒池内段。
- Alembic 0017:segments 表 narrative_defer_* 列。
- ProfileAgent:经 LlmGateway/注入 Provider 统一聊天与 JSON,新增测试。
- ImagePromptOrchestrator:LLM 初始化失败可依配置降级或硬失败;补充策略测试。
- 配套单测与 README/本地开发文档表述更新。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-06 13:18:02 +08:00

86 lines
2.6 KiB
Python
Raw 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.
"""回忆录两阶段管线Phase2 触发条件与 orchestrator 字段。"""
from unittest.mock import MagicMock
import pytest
from app.agents.memoir.orchestrator import MemoirOrchestrator
from app.agents.memoir.extraction_agent import ExtractionResult
from app.agents.memoir.classification_agent import ChapterClassifyResult
from app.agents.state_schema import MemoirStateSchema
from app.tasks.memoir_tasks import _should_trigger_phase2
def test_segment_chapter_category_populated() -> None:
orch = MemoirOrchestrator()
orch.extraction_agent.extract = MagicMock(
return_value=ExtractionResult(
detected_stage="childhood", slots={"daily_life": "玩布娃娃"}
)
)
orch.classification_agent.classify = MagicMock(
return_value=ChapterClassifyResult(category="childhood", llm_said_none=False)
)
st = MemoirStateSchema(
stage_order=["childhood"],
current_stage="childhood",
covered_stages=[],
slots={},
)
def get_state() -> MemoirStateSchema:
return st
def update_slot(
stage: str, slot_name: str, snippet: str, seg_ids: list[str]
) -> MemoirStateSchema:
return st
class _Seg:
def __init__(self, sid: str, text: str) -> None:
self.id = sid
self.user_input_text = text
s1 = _Seg("a1", "小时候喜欢玩布娃娃")
p = orch.prepare_batches(
segments=[s1],
llm=MagicMock(),
get_or_create_state=get_state,
update_slot=update_slot,
)
assert p.segment_chapter_category["a1"] == "childhood"
@pytest.mark.parametrize(
"count,total_chars,current_chars,expect",
[
(1, 10, 60, True), # immediate via current segment chars
(3, 5, 5, True), # batch min segments
(2, 100, 5, True), # batch min total chars
(2, 50, 5, False), # below both accum thresholds
],
)
def test_should_trigger_phase2_matrix(
monkeypatch: pytest.MonkeyPatch,
count: int,
total_chars: int,
current_chars: int,
expect: bool,
) -> None:
monkeypatch.setattr(
"app.tasks.memoir_tasks.settings.memoir_narrative_immediate_char_threshold",
50,
)
monkeypatch.setattr(
"app.tasks.memoir_tasks.settings.memoir_narrative_batch_min_segments",
3,
)
monkeypatch.setattr(
"app.tasks.memoir_tasks.settings.memoir_narrative_batch_min_chars",
80,
)
db = MagicMock()
db.execute.return_value.one.return_value = (count, total_chars)
assert _should_trigger_phase2(db, "user-1", "childhood", current_chars) == expect