feat(backends): wire LangGraph backend behind AGENT_BACKEND switch (assignment 2)
- src/backends/langgraph.py: LangGraphBackend implements ChatBackend; complete() runs one graph turn per session (thread_id=sessionId) and returns state-prefixed content; stream() emits the full state prefix before body fragments (full-answer slicing, not yet real incremental output - assignment 8) - src/core/config.py: AGENT_BACKEND=fastgpt|langgraph, langgraph mode no longer requires FastGPT credentials at startup - src/core/fastgpt_client.py: lifespan builds the backend by switch - test/backends/test_langgraph_backend.py: adapter acceptance tests Verified: - pytest: 48 passed - AGENT_BACKEND=langgraph uvicorn: POST /chat -> 1002; browser_addon -> browser_addon.1002; SSE = stage_code, text_delta, single done Known limit: stream slices a complete answer; no formUpdate deltas yet (assignment 6).
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"""作业 2 验收测试:LangGraph 后端适配器。
|
||||
|
||||
通过标准(对应迁移计划作业 2):
|
||||
- ``complete()`` 返回带 ``<state>1002</state>`` 前缀的 ``ChatResult``,
|
||||
API 层据此解析阶段码;
|
||||
- ``stream()`` 先产出含完整 state 前缀的 ``TextDelta``,再产出正文,
|
||||
保证 ``stage_code`` 事件先于正文;
|
||||
- 会话通过 ``thread_id = session_id`` 绑定(图 checkpoint 语义)。
|
||||
|
||||
注意:``src/backends/langgraph.py`` 中的 TODO 完成前,
|
||||
本文件的测试会失败,属于预期状态。
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from src.backends.chat import ChatInput, TextDelta
|
||||
from src.backends.langgraph import LangGraphBackend
|
||||
|
||||
|
||||
def make_input() -> ChatInput:
|
||||
return ChatInput(
|
||||
session_id="session-001",
|
||||
text="发生了交通事故",
|
||||
client_mode="direct",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_complete_returns_state_prefixed_result() -> None:
|
||||
backend = LangGraphBackend()
|
||||
|
||||
result = await backend.complete(make_input())
|
||||
|
||||
assert result.content.startswith("<state>1002</state>")
|
||||
assert "发生了交通事故" in result.content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stream_emits_prefix_before_body() -> None:
|
||||
backend = LangGraphBackend()
|
||||
|
||||
events = [event async for event in backend.stream(make_input())]
|
||||
|
||||
assert events, "stream 至少应产生一个 TextDelta"
|
||||
assert all(isinstance(event, TextDelta) for event in events)
|
||||
joined = "".join(event.text for event in events)
|
||||
assert joined.startswith("<state>1002</state>")
|
||||
assert "发生了交通事故" in joined
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sessions_are_isolated_by_thread_id() -> None:
|
||||
backend = LangGraphBackend()
|
||||
|
||||
first = await backend.complete(make_input())
|
||||
second = await backend.complete(make_input())
|
||||
other = await backend.complete(
|
||||
ChatInput(session_id="session-002", text="你好", client_mode="direct")
|
||||
)
|
||||
|
||||
assert first.content != "" and second.content != ""
|
||||
assert "你好" in other.content
|
||||
Reference in New Issue
Block a user