Skip to content

Commit ed9568f

Browse files
committed
add stream to rag agent + set it to only vector store id support
1 parent 68f7223 commit ed9568f

6 files changed

Lines changed: 155 additions & 78 deletions

File tree

agents/community/langgraph_agentic_rag/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,32 @@ oc get route langgraph-agentic-rag -o jsonpath='{.spec.host}'
222222

223223
Send a test request:
224224

225+
/chat endpoint
226+
225227
```bash
226228
curl -X POST https://<YOUR_ROUTE_URL>/chat \
227229
-H "Content-Type: application/json" \
228230
-d '{"message": "What is LangChain?"}'
229231
```
230232

233+
/stream endpoint
234+
Classic Print
235+
236+
```bash
237+
curl -X POST https://<YOUR_ROUTE_URL>/stream \
238+
-H "Content-Type: application/json" \
239+
-d '{"message": "What is LangChain?"}'
240+
```
241+
242+
Pretty Printed Stream
243+
244+
```bash
245+
curl -X POST https://<YOUR_ROUTE_URL>/stream \
246+
-H "Content-Type: application/json" \
247+
-d '{"message": "What is LangChain?"}' |
248+
jq -R -r -j --stream 'scan("^data:(.*)")[] | fromjson.content // empty'
249+
```
250+
231251
## Agent-Specific Documentation
232252

233253
### Additional Resources

agents/community/langgraph_agentic_rag/data/load_documents.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Script to load documents from text files into a vector store via LlamaStack.
33
44
If VECTOR_STORE_ID is set, documents are added to the existing store.
5-
Otherwise a new vector store is created using VECTOR_STORE_NAME,
5+
Otherwise, a new vector store is created using VECTOR_STORE_NAME,
66
its ID is printed and written back into the .env file.
77
"""
88

@@ -22,7 +22,9 @@
2222

2323
def update_env_file(key: str, value: str):
2424
"""Update or add a key=value pair in the .env file next to this script."""
25-
env_path = Path(__file__).resolve().parent.parent / ".env" # data/ -> langgraph_agentic_rag/.env
25+
env_path = (
26+
Path(__file__).resolve().parent.parent / ".env"
27+
) # data/ -> langgraph_agentic_rag/.env
2628
if not env_path.exists():
2729
env_path.write_text(f"{key}={value}\n")
2830
return
@@ -78,7 +80,6 @@ def load_and_index_documents(
7880
)
7981

8082
vector_store_id = getenv("VECTOR_STORE_ID")
81-
vector_store_name = getenv("VECTOR_STORE_NAME") or "my_vector_store"
8283
provider_id = "milvus"
8384
embedding_dimension = 768
8485

@@ -88,19 +89,19 @@ def load_and_index_documents(
8889
else:
8990
# Create a new vector store
9091
vector_store = client.vector_stores.create(
91-
name=vector_store_name,
9292
extra_body={
9393
"provider_id": provider_id,
9494
"embedding_model": embedding_model,
9595
"embedding_dimension": embedding_dimension,
9696
},
9797
)
9898
vector_store_id = vector_store.id
99-
print(f"Vector store created: id={vector_store_id} name={vector_store_name}")
99+
print(f"Vector store created: id={vector_store_id} name={vector_store.name}")
100100

101101
# Persist the new ID to .env
102102
update_env_file("VECTOR_STORE_ID", vector_store_id)
103103
print(f"Updated .env with VECTOR_STORE_ID={vector_store_id}")
104+
print("NOTE!: Please use `source ./init.sh' to update the env variables.")
104105

105106
print("Loading documents from directory...")
106107
loader = TextLoader(docs_to_load)
@@ -159,8 +160,10 @@ def load_and_index_documents(
159160
vector_store_id=vector_store_id,
160161
)
161162

162-
print(f"Done! {len(formatted_chunks)} chunks inserted into vector store {vector_store_id}")
163+
print(
164+
f"Done! {len(formatted_chunks)} chunks inserted into vector store {vector_store_id}"
165+
)
163166

164167

165168
if __name__ == "__main__":
166-
load_and_index_documents()
169+
load_and_index_documents()

agents/community/langgraph_agentic_rag/k8s/deployment.yaml

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -25,58 +25,56 @@ spec:
2525
app: langgraph-agentic-rag
2626
spec:
2727
containers:
28-
- name: langgraph-agentic-rag
29-
image: "${CONTAINER_IMAGE}"
30-
ports:
31-
- containerPort: 8080
32-
name: http
33-
env:
34-
- name: PORT
35-
value: "8080"
36-
# API_KEY from Secret (secure)
37-
- name: API_KEY
38-
valueFrom:
39-
secretKeyRef:
40-
name: langgraph-agentic-rag-secrets
41-
key: api-key
42-
- name: BASE_URL
43-
value: "${BASE_URL}"
44-
- name: MODEL_ID
45-
value: "${MODEL_ID}"
46-
- name: EMBEDDING_MODEL
47-
value: "${EMBEDDING_MODEL}"
48-
- name: VECTOR_STORE_NAME
49-
value: "${VECTOR_STORE_NAME}"
50-
- name: VECTOR_STORE_ID
51-
value: "${VECTOR_STORE_ID}"
52-
- name: VECTOR_STORE_PATH
53-
value: "/app/data/vector_store"
54-
volumeMounts:
55-
- name: vector-store
56-
mountPath: /app/data
57-
resources:
58-
requests:
59-
memory: "512Mi"
60-
cpu: "200m"
61-
limits:
62-
memory: "1Gi"
63-
cpu: "1000m"
64-
livenessProbe:
65-
httpGet:
66-
path: /health
67-
port: 8080
68-
initialDelaySeconds: 15
69-
periodSeconds: 120
70-
timeoutSeconds: 5
71-
failureThreshold: 3
72-
readinessProbe:
73-
httpGet:
74-
path: /health
75-
port: 8080
76-
initialDelaySeconds: 10
77-
periodSeconds: 30
78-
timeoutSeconds: 3
79-
failureThreshold: 3
28+
- name: langgraph-agentic-rag
29+
image: "${CONTAINER_IMAGE}"
30+
ports:
31+
- containerPort: 8080
32+
name: http
33+
env:
34+
- name: PORT
35+
value: "8080"
36+
# API_KEY from Secret (secure)
37+
- name: API_KEY
38+
valueFrom:
39+
secretKeyRef:
40+
name: langgraph-agentic-rag-secrets
41+
key: api-key
42+
- name: BASE_URL
43+
value: "${BASE_URL}"
44+
- name: MODEL_ID
45+
value: "${MODEL_ID}"
46+
- name: EMBEDDING_MODEL
47+
value: "${EMBEDDING_MODEL}"
48+
- name: VECTOR_STORE_ID
49+
value: "${VECTOR_STORE_ID}"
50+
- name: VECTOR_STORE_PATH
51+
value: "/app/data/vector_store"
52+
volumeMounts:
53+
- name: vector-store
54+
mountPath: /app/data
55+
resources:
56+
requests:
57+
memory: "512Mi"
58+
cpu: "200m"
59+
limits:
60+
memory: "1Gi"
61+
cpu: "1000m"
62+
livenessProbe:
63+
httpGet:
64+
path: /health
65+
port: 8080
66+
initialDelaySeconds: 15
67+
periodSeconds: 120
68+
timeoutSeconds: 5
69+
failureThreshold: 3
70+
readinessProbe:
71+
httpGet:
72+
path: /health
73+
port: 8080
74+
initialDelaySeconds: 10
75+
periodSeconds: 30
76+
timeoutSeconds: 3
77+
failureThreshold: 3
8078
volumes:
81-
- name: vector-store
82-
emptyDir: {}
79+
- name: vector-store
80+
emptyDir: { }

agents/community/langgraph_agentic_rag/main.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from os import getenv
44

55
from fastapi import FastAPI, HTTPException
6+
from fastapi.responses import StreamingResponse
67
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
78
from pydantic import BaseModel
89

@@ -141,6 +142,69 @@ async def chat(request: ChatRequest):
141142
)
142143

143144

145+
@app.post("/stream")
146+
async def stream(request: ChatRequest):
147+
"""
148+
Streaming chat endpoint that accepts a message and returns the agent's
149+
response as Server-Sent Events (SSE).
150+
151+
Event types:
152+
- token: streamed text token from the LLM
153+
- tool_call: tool invocation by the agent
154+
- tool_result: result returned by a tool
155+
- done: signals the stream is complete
156+
157+
Args:
158+
request: ChatRequest containing the user message
159+
"""
160+
global agent_graph
161+
162+
if agent_graph is None:
163+
raise HTTPException(status_code=503, detail="Agent not initialized")
164+
165+
async def event_generator():
166+
try:
167+
messages = [HumanMessage(content=request.message)]
168+
169+
async for event in agent_graph.astream_events(
170+
{"messages": messages},
171+
config={"recursion_limit": 15},
172+
version="v2",
173+
):
174+
kind = event["event"]
175+
176+
# LLM streaming tokens
177+
if kind == "on_chat_model_stream":
178+
chunk = event["data"]["chunk"]
179+
if chunk.content:
180+
yield f"event: token\ndata: {json.dumps({'content': chunk.content})}\n\n"
181+
182+
# Complete tool call (after LLM finishes generating the call)
183+
elif kind == "on_chat_model_end":
184+
message = event["data"]["output"]
185+
if hasattr(message, "tool_calls") and message.tool_calls:
186+
for tc in message.tool_calls:
187+
yield f"event: tool_call\ndata: {json.dumps({'name': tc['name'], 'args': tc['args']})}\n\n"
188+
189+
# Tool execution results
190+
elif kind == "on_tool_end":
191+
output = event["data"].get("output", "")
192+
if hasattr(output, "content"):
193+
output = output.content
194+
yield f"event: tool_result\ndata: {json.dumps({'name': event.get('name', ''), 'output': str(output)})}\n\n"
195+
196+
yield "event: done\ndata: {}\n\n"
197+
198+
except Exception as e:
199+
yield f"event: error\ndata: {json.dumps({'detail': str(e)})}\n\n"
200+
201+
return StreamingResponse(
202+
event_generator(),
203+
media_type="text/event-stream",
204+
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
205+
)
206+
207+
144208
@app.get("/health")
145209
async def health():
146210
"""Return service health and whether the agent graph has been initialized."""

agents/community/langgraph_agentic_rag/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies = [
2020
"milvus-lite>=2.5.1",
2121
"python-dotenv>=1.2.1",
2222
"setuptools>=80.9.0,<82.0.0",
23+
"ipykernel>=7.2.0",
2324
]
2425

2526
[project.optional-dependencies]
@@ -32,4 +33,4 @@ where = ["src"]
3233

3334
[build-system]
3435
requires = ["setuptools>=80.9.0"]
35-
build-backend = "setuptools.build_meta"
36+
build-backend = "setuptools.build_meta"

agents/community/langgraph_agentic_rag/src/langgraph_agentic_rag/tools.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,20 @@ def get_retriever_components(
3232
# Get configuration from environment if not provided
3333
if not base_url:
3434
base_url = getenv("BASE_URL")
35-
vector_store_name = getenv("VECTOR_STORE_NAME")
35+
vector_store_id = getenv("VECTOR_STORE_ID")
36+
if not vector_store_id:
37+
raise RuntimeError(
38+
"VECTOR_STORE_ID env var is not set. Run load_documents.py first."
39+
"or check if you provided right ID"
40+
)
3641

3742
# Initialize LlamaStack client
3843
client = LlamaStackClient(
3944
base_url=base_url,
4045
api_key=getenv("API_KEY"),
4146
)
4247

43-
# Get the vector store ID by name
44-
vector_store_list = client.vector_stores.list()
45-
vector_store_id = None
46-
47-
for vs in vector_store_list.data:
48-
if vs.name == vector_store_name:
49-
print(f"Your Vector Store: {vs.id} ({vs.name})")
50-
vector_store_id = vs.id
51-
52-
if not vector_store_id:
53-
available = [f"{vs.name} ({vs.id})" for vs in vector_store_list.data]
54-
raise RuntimeError(
55-
f"Vector store '{vector_store_name}' not found. "
56-
f"Available: {available}. Run load_documents.py first."
57-
)
48+
print(f"Using vector store: {vector_store_id}")
5849

5950
# Cache the components
6051
_client_cache = client

0 commit comments

Comments
 (0)