From b86c1304115937c8ec059321a7cfb883724c8d6a Mon Sep 17 00:00:00 2001 From: Hao Luo <338265+howlowck@users.noreply.github.com> Date: Wed, 3 Dec 2025 15:41:27 -1000 Subject: [PATCH] Python: Fixed empty text content pydantic validation failure (#2539) * Fixed empty text content pydantic validation failure * simplify syntax. renamed test --------- Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> --- .../ag-ui/agent_framework_ag_ui/_events.py | 7 +++++ .../ag-ui/tests/test_events_comprehensive.py | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/python/packages/ag-ui/agent_framework_ag_ui/_events.py b/python/packages/ag-ui/agent_framework_ag_ui/_events.py index f5a6c77029..184da0239e 100644 --- a/python/packages/ag-ui/agent_framework_ag_ui/_events.py +++ b/python/packages/ag-ui/agent_framework_ag_ui/_events.py @@ -134,6 +134,13 @@ class AgentFrameworkEventBridge: logger.info(f" Suppressed summary length={len(self.suppressed_summary)}") return events + # Skip empty text chunks to avoid emitting + # TextMessageContentEvent with an empty `delta` which fails + # Pydantic validation (AG-UI requires non-empty strings). + if not content.text: + logger.info(" SKIPPING TextContent: empty chunk") + return events + if not self.current_message_id: self.current_message_id = generate_event_id() start_event = TextMessageStartEvent( diff --git a/python/packages/ag-ui/tests/test_events_comprehensive.py b/python/packages/ag-ui/tests/test_events_comprehensive.py index cd2663bd3c..a51d1f382a 100644 --- a/python/packages/ag-ui/tests/test_events_comprehensive.py +++ b/python/packages/ag-ui/tests/test_events_comprehensive.py @@ -68,6 +68,37 @@ async def test_skip_text_content_for_structured_outputs(): assert len(events) == 0 +async def test_skip_text_content_for_empty_text(): + """Test streaming TextContent with empty chunks.""" + from agent_framework_ag_ui._events import AgentFrameworkEventBridge + + bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread") + + update1 = AgentRunResponseUpdate(contents=[TextContent(text="Hello ")]) + update2 = AgentRunResponseUpdate(contents=[TextContent(text="")]) # Empty chunk + update3 = AgentRunResponseUpdate(contents=[TextContent(text="world")]) + + events1 = await bridge.from_agent_run_update(update1) + events2 = await bridge.from_agent_run_update(update2) + events3 = await bridge.from_agent_run_update(update3) + + # First update: START + CONTENT + assert len(events1) == 2 + assert events1[0].type == "TEXT_MESSAGE_START" + assert events1[1].delta == "Hello " + + # Second update: should skip empty chunk, no events + assert len(events2) == 0 + + # Third update: just CONTENT (same message) + assert len(events3) == 1 + assert events3[0].type == "TEXT_MESSAGE_CONTENT" + assert events3[0].delta == "world" + + # Both content events should have same message_id + assert events1[1].message_id == events3[0].message_id + + async def test_tool_call_with_name(): """Test FunctionCallContent with name emits ToolCallStartEvent.""" from agent_framework_ag_ui._events import AgentFrameworkEventBridge