refactor(agent): inject deps via context_schema, LLM-driven photo/confirm nodes
- graph.py: LangGraph-native DI - StateGraph(context_schema=GraphDeps), nodes take (state, runtime: Runtime[GraphDeps]); build_graph() decoupled from LLM; deleted scaffold _event_reply/_fake_llm_propose/stub nodes - photo.py: model proposes stage+body via per-mode prompts (direct/addon); validator enforces clientMode exits; deterministic event forcing removed - confirm.py: single runtime for single/dual confirm, extraction call isolated per person_index - llm.py: workflow_fake_llm() (offline workflow-faithful responder, history-aware) for smoke/tests - backend: LangGraphBackend(graph, llm) passes context per call - tests: invoke helpers inject GraphDeps; illegal-proposal test asserts error + state untouched via second well-behaved fake Verified: 143 passed; smoke turns 1/2/1 with original welcome text
This commit is contained in:
@@ -12,8 +12,9 @@
|
||||
|
||||
import pytest
|
||||
|
||||
import src.agent.graph as graph_mod
|
||||
from src.agent.graph import build_graph
|
||||
from src.agent.llm import FakeLLMService
|
||||
|
||||
from src.agent.graph import GraphDeps, build_graph, workflow_fake_llm
|
||||
from src.agent.transitions import (
|
||||
TERMINAL_STAGES,
|
||||
TransitionSpec,
|
||||
@@ -21,12 +22,13 @@ from src.agent.transitions import (
|
||||
)
|
||||
|
||||
|
||||
def invoke(graph, thread_id, text, **initial):
|
||||
def invoke(graph, thread_id, text, llm=None, **initial):
|
||||
payload = {"session_id": thread_id, "input_text": text}
|
||||
payload.update(initial)
|
||||
return graph.ainvoke(
|
||||
payload,
|
||||
config={"configurable": {"thread_id": thread_id}},
|
||||
context=GraphDeps(llm=llm or workflow_fake_llm()),
|
||||
)
|
||||
|
||||
|
||||
@@ -82,12 +84,21 @@ async def test_valid_reply_resets_no_reply_count():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_illegal_proposal_keeps_stage(monkeypatch):
|
||||
# LLM 提议非法跳步(2001 → 2003):校验拒绝,状态不被污染
|
||||
async def test_illegal_proposal_raises_and_state_untouched():
|
||||
# LLM 坚持非法跳步(2001 → 2003):白名单拒绝 + 一次修复后
|
||||
# 仍非法 → CollectReplyError,非法阶段不写状态
|
||||
from src.agent.collect import CollectReplyError
|
||||
|
||||
llm = FakeLLMService(lambda s, u: "<state>2003</state>跳步")
|
||||
graph = build_graph()
|
||||
monkeypatch.setattr(graph_mod, "_fake_llm_propose", lambda s, e: "2003")
|
||||
result = await invoke(graph, "t-illegal", "【拍摄完成】", stage_code="2001")
|
||||
assert result["stage_code"] == "2001"
|
||||
with pytest.raises(CollectReplyError):
|
||||
await invoke(graph, "t-illegal", "【拍摄完成】", llm=llm, stage_code="2001")
|
||||
assert len(llm.calls) == 2
|
||||
|
||||
# 会话未被污染:下一轮用守规矩的 fake 继续,阶段仍是 2001
|
||||
good = FakeLLMService(lambda s, u: "<state>2002</state>已收到。")
|
||||
result = await invoke(graph, "t-illegal", "【拍摄完成】", llm=good, stage_code="2001")
|
||||
assert result["stage_code"] == "2002"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user