diff --git a/python/packages/ag-ui/agent_framework_ag_ui/_http_service.py b/python/packages/ag-ui/agent_framework_ag_ui/_http_service.py index f13d749080e..e4751dfd884 100644 --- a/python/packages/ag-ui/agent_framework_ag_ui/_http_service.py +++ b/python/packages/ag-ui/agent_framework_ag_ui/_http_service.py @@ -269,8 +269,11 @@ async def exit_response( async for line in response.aiter_lines(): # Parse Server-Sent Events format - if line.startswith("data: "): - data = line[6:] # Remove "data: " prefix + if line.startswith("data:"): + data = line[5:] + # The space after "data:" is optional in SSE. + if data.startswith(" "): + data = data[1:] try: event = json.loads(data) logger.debug(f"Received event: {event.get('type', 'UNKNOWN')}") diff --git a/python/packages/ag-ui/tests/ag_ui/test_http_service.py b/python/packages/ag-ui/tests/ag_ui/test_http_service.py index a758c2abaa5..fbf4f9a8bfd 100644 --- a/python/packages/ag-ui/tests/ag_ui/test_http_service.py +++ b/python/packages/ag-ui/tests/ag_ui/test_http_service.py @@ -317,6 +317,20 @@ async def mock_aiter_lines(): assert events[0]["type"] == "RUN_FINISHED" +async def test_post_run_accepts_data_lines_without_space(sample_events): + """The space after "data:" is optional in SSE, so "data:{...}" lines must be parsed too.""" + body = "".join(f"data:{json.dumps(event)}\n\n" for event in sample_events) + + def handler(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, headers={"content-type": "text/event-stream"}, text=body) + + async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client: + service = AGUIHttpService("http://localhost:8888/", http_client=client) + events = [event async for event in service.post_run(thread_id="thread_123", run_id="run_456", messages=[])] + + assert events == sample_events + + async def test_context_manager(): """Test context manager functionality.""" async with AGUIHttpService("http://localhost:8888/") as service: