Skip to content

Commit f8eee76

Browse files
committed
add stream to websearch agent
1 parent ed9568f commit f8eee76

4 files changed

Lines changed: 95 additions & 2 deletions

File tree

agents/base/langgraph_react_agent/main.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import logging
23
from contextlib import asynccontextmanager
34
from os import getenv
45

@@ -7,6 +8,8 @@
78
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
89
from pydantic import BaseModel
910

11+
logger = logging.getLogger(__name__)
12+
1013
from langgraph_react_agent_base.agent import get_graph_closure
1114

1215

@@ -193,7 +196,8 @@ async def event_generator():
193196
yield "event: done\ndata: {}\n\n"
194197

195198
except Exception as e:
196-
yield f"event: error\ndata: {json.dumps({'detail': str(e)})}\n\n"
199+
logger.exception("Error in stream event_generator")
200+
yield f"event: error\ndata: {json.dumps({'detail': 'Internal server error'})}\n\n"
197201

198202
return StreamingResponse(
199203
event_generator(),

agents/base/llamaindex_websearch_agent/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,32 @@ oc get route llamaindex-websearch-agent -o jsonpath='{.spec.host}'
183183

184184
Send a test request:
185185

186+
/chat endpoint
187+
186188
```bash
187189
curl -X POST https://<YOUR_ROUTE_URL>/chat \
188190
-H "Content-Type: application/json" \
189191
-d '{"message": "Which company is consider the best?"}'
190192
```
191193

194+
/stream endpoint
195+
Classic Print
196+
197+
```bash
198+
curl -X POST https://<YOUR_ROUTE_URL>/stream \
199+
-H "Content-Type: application/json" \
200+
-d '{"message": "Which company is consider the best?"}'
201+
```
202+
203+
Pretty Printed Stream
204+
205+
```bash
206+
curl -X POST https://<YOUR_ROUTE_URL>/stream \
207+
-H "Content-Type: application/json" \
208+
-d '{"message": "Which company is consider the best?"}' |
209+
jq -R -r -j --stream 'scan("^data:(.*)")[] | fromjson.content // empty'
210+
```
211+
192212
---
193213

194214
## Agent-Specific Documentation

agents/base/llamaindex_websearch_agent/main.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import json
2+
import logging
23
from contextlib import asynccontextmanager
34
from os import getenv
45

56
from fastapi import FastAPI, HTTPException
7+
from fastapi.responses import StreamingResponse
68
from llama_index_workflow_agent_base.agent import get_workflow_closure
9+
from llama_index_workflow_agent_base.workflow import ToolCallEvent, InputEvent
710
from pydantic import BaseModel
811

12+
logger = logging.getLogger(__name__)
13+
914

1015
# Request/Response models
1116
class ChatRequest(BaseModel):
@@ -189,6 +194,66 @@ async def chat(request: ChatRequest):
189194
)
190195

191196

197+
@app.post("/stream")
198+
async def stream(request: ChatRequest):
199+
"""
200+
Streaming chat endpoint that accepts a message and returns the agent's
201+
response as Server-Sent Events (SSE).
202+
203+
Event types:
204+
- tool_call: tool invocation by the agent
205+
- tool_result: result returned by a tool
206+
- token: final answer text
207+
- done: signals the stream is complete
208+
209+
Args:
210+
request: ChatRequest containing the user message
211+
"""
212+
global get_agent
213+
214+
if get_agent is None:
215+
raise HTTPException(status_code=503, detail="Agent not initialized")
216+
217+
async def event_generator():
218+
try:
219+
agent = get_agent()
220+
messages = [{"role": "user", "content": request.message}]
221+
222+
handler = agent.run(input=messages)
223+
224+
async for event in handler.stream_events():
225+
if isinstance(event, ToolCallEvent):
226+
for tc in event.tool_calls:
227+
yield f"event: tool_call\ndata: {json.dumps({'name': tc.tool_name, 'args': tc.tool_kwargs})}\n\n"
228+
229+
elif isinstance(event, InputEvent):
230+
# Check if the last message is a tool result
231+
if event.input:
232+
last_msg = event.input[-1]
233+
if getattr(last_msg, "role", None) == "tool":
234+
additional = getattr(last_msg, "additional_kwargs", {}) or {}
235+
yield f"event: tool_result\ndata: {json.dumps({'name': additional.get('name', ''), 'output': _get_message_content(last_msg)})}\n\n"
236+
237+
result = await handler
238+
# Extract final answer from the result
239+
if result and "response" in result:
240+
content = _get_message_content(result["response"].message)
241+
if content:
242+
yield f"event: token\ndata: {json.dumps({'content': content})}\n\n"
243+
244+
yield "event: done\ndata: {}\n\n"
245+
246+
except Exception as e:
247+
logger.exception("Error in stream event_generator")
248+
yield f"event: error\ndata: {json.dumps({'detail': 'Internal server error'})}\n\n"
249+
250+
return StreamingResponse(
251+
event_generator(),
252+
media_type="text/event-stream",
253+
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
254+
)
255+
256+
192257
@app.get("/health")
193258
async def health():
194259
"""Return service health and whether the workflow closure has been initialized."""

agents/community/langgraph_agentic_rag/main.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import logging
23
from contextlib import asynccontextmanager
34
from os import getenv
45

@@ -7,6 +8,8 @@
78
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
89
from pydantic import BaseModel
910

11+
logger = logging.getLogger(__name__)
12+
1013
from langgraph_agentic_rag.agent import get_graph_closure
1114

1215

@@ -196,7 +199,8 @@ async def event_generator():
196199
yield "event: done\ndata: {}\n\n"
197200

198201
except Exception as e:
199-
yield f"event: error\ndata: {json.dumps({'detail': str(e)})}\n\n"
202+
logger.exception("Error in stream event_generator")
203+
yield f"event: error\ndata: {json.dumps({'detail': 'Internal server error'})}\n\n"
200204

201205
return StreamingResponse(
202206
event_generator(),

0 commit comments

Comments
 (0)