184 lines
6.7 KiB
Python
184 lines
6.7 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from agent_integration.client import OpenAgentEvent
|
|
from agent_integration.events import AgentStreamNormalizer
|
|
|
|
|
|
class AgentStreamNormalizerTests(unittest.TestCase):
|
|
def test_live_langgraph_shape_exposes_only_visible_answer(self):
|
|
normalizer = AgentStreamNormalizer()
|
|
raw_events = [
|
|
OpenAgentEvent(
|
|
event="metadata",
|
|
data={"run_id": "run-001", "thread_id": "internal-thread"},
|
|
event_id="1",
|
|
),
|
|
OpenAgentEvent(
|
|
event="values",
|
|
data={
|
|
"messages": [{"type": "human", "content": "hello"}],
|
|
"thread_data": {"workspace_path": "/internal/secret/path"},
|
|
},
|
|
),
|
|
OpenAgentEvent(
|
|
event="messages",
|
|
data=[
|
|
{
|
|
"type": "AIMessageChunk",
|
|
"content": "",
|
|
"additional_kwargs": {"reasoning_content": "INTERNAL_REASONING"},
|
|
},
|
|
{"langgraph_node": "model"},
|
|
],
|
|
),
|
|
OpenAgentEvent(
|
|
event="messages",
|
|
data=[
|
|
{"type": "AIMessageChunk", "content": "INTERNAL_TITLE"},
|
|
{"langgraph_node": "TitleMiddleware.after_model", "tags": ["middleware:title"]},
|
|
],
|
|
),
|
|
OpenAgentEvent(
|
|
event="messages",
|
|
data=[
|
|
{"type": "AIMessageChunk", "content": "连接"},
|
|
{"langgraph_node": "model"},
|
|
],
|
|
event_id="2",
|
|
),
|
|
OpenAgentEvent(
|
|
event="messages",
|
|
data=[
|
|
{"type": "AIMessageChunk", "content": "成功"},
|
|
{"langgraph_node": "model"},
|
|
],
|
|
event_id="3",
|
|
),
|
|
OpenAgentEvent(
|
|
event="messages",
|
|
data=[
|
|
{
|
|
"type": "ai",
|
|
"content": "连接成功",
|
|
"additional_kwargs": {"reasoning_content": "INTERNAL_REASONING"},
|
|
},
|
|
{"langgraph_node": "model"},
|
|
],
|
|
),
|
|
OpenAgentEvent(
|
|
event="values",
|
|
data={
|
|
"messages": [
|
|
{"type": "human", "content": "hello"},
|
|
{
|
|
"type": "ai",
|
|
"content": "连接成功",
|
|
"additional_kwargs": {"reasoning_content": "INTERNAL_REASONING"},
|
|
},
|
|
],
|
|
"thread_data": {"workspace_path": "/internal/secret/path"},
|
|
},
|
|
),
|
|
OpenAgentEvent(
|
|
event="error",
|
|
data={
|
|
"code": "open_agent_final_content_missing",
|
|
"message": "Open agent run completed without final content",
|
|
"retryable": True,
|
|
"run_id": "run-001",
|
|
"request_id": "request-001",
|
|
"internal_trace": "DO_NOT_EXPOSE",
|
|
},
|
|
),
|
|
OpenAgentEvent(event="end", data=None, event_id="4"),
|
|
]
|
|
|
|
public_events = []
|
|
for event in raw_events:
|
|
public_events.extend(normalizer.feed(event))
|
|
public_events.extend(normalizer.finish())
|
|
|
|
self.assertEqual(
|
|
[event.event for event in public_events],
|
|
[
|
|
"run.started",
|
|
"message.delta",
|
|
"message.delta",
|
|
"message.completed",
|
|
"run.warning",
|
|
"run.end",
|
|
],
|
|
)
|
|
self.assertEqual(public_events[0].data, {"run_id": "run-001"})
|
|
self.assertEqual(public_events[1].data, {"content": "连接"})
|
|
self.assertEqual(public_events[2].data, {"content": "成功"})
|
|
self.assertEqual(
|
|
public_events[3].data,
|
|
{"content": "连接成功", "streamed": True},
|
|
)
|
|
self.assertEqual(public_events[4].event, "run.warning")
|
|
self.assertEqual(public_events[5].data["status"], "completed_with_warning")
|
|
self.assertFalse(normalizer.fatal_error)
|
|
self.assertEqual(normalizer.final_content, "连接成功")
|
|
rendered = repr([event.to_dict() for event in public_events])
|
|
self.assertNotIn("INTERNAL_REASONING", rendered)
|
|
self.assertNotIn("INTERNAL_TITLE", rendered)
|
|
self.assertNotIn("secret/path", rendered)
|
|
self.assertNotIn("DO_NOT_EXPOSE", rendered)
|
|
|
|
def test_final_content_missing_without_visible_content_is_fatal(self):
|
|
normalizer = AgentStreamNormalizer()
|
|
normalizer.feed(
|
|
OpenAgentEvent(
|
|
event="error",
|
|
data={
|
|
"code": "open_agent_final_content_missing",
|
|
"message": "missing",
|
|
"retryable": True,
|
|
},
|
|
)
|
|
)
|
|
public_events = normalizer.feed(OpenAgentEvent(event="end", data=None))
|
|
|
|
self.assertTrue(normalizer.fatal_error)
|
|
self.assertEqual([event.event for event in public_events], ["run.error", "run.end"])
|
|
self.assertEqual(public_events[-1].data["status"], "failed")
|
|
|
|
def test_values_final_message_is_fallback_when_no_deltas_arrive(self):
|
|
normalizer = AgentStreamNormalizer()
|
|
normalizer.feed(
|
|
OpenAgentEvent(
|
|
event="values",
|
|
data={"messages": [{"type": "ai", "content": "完整答案"}]},
|
|
)
|
|
)
|
|
public_events = normalizer.finish()
|
|
|
|
self.assertEqual(public_events[0].event, "message.completed")
|
|
self.assertEqual(
|
|
public_events[0].data,
|
|
{"content": "完整答案", "streamed": False},
|
|
)
|
|
self.assertFalse(normalizer.fatal_error)
|
|
|
|
def test_documented_message_delta_shape_remains_supported(self):
|
|
normalizer = AgentStreamNormalizer()
|
|
public_events = normalizer.feed(
|
|
OpenAgentEvent(event="message.delta", data={"content": "你"})
|
|
)
|
|
normalizer.feed(
|
|
OpenAgentEvent(event="run.completed", data={"status": "completed"})
|
|
)
|
|
public_events.extend(normalizer.finish())
|
|
|
|
self.assertEqual(public_events[0].data, {"content": "你"})
|
|
self.assertEqual(public_events[1].data, {"content": "你", "streamed": True})
|
|
self.assertEqual(public_events[2].data["status"], "completed")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|
|
|