Skip to content

Commit 6683274

Browse files
feature: stream endpoint added
1 parent 1258951 commit 6683274

4 files changed

Lines changed: 475 additions & 37 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Full setup guide: [docs/RUNBOOK.md](docs/RUNBOOK.md)
5656
| `GET` | `/v1/documents/{id}` | Admin (own company) or Super Admin |
5757
| `DELETE` | `/v1/documents/{id}` | Admin (own company) or Super Admin |
5858
| `POST` | `/v1/chat/invoke` | Admin or Employee |
59+
| `POST` | `/v1/chat/stream` | Admin or Employee |
5960
| `GET` | `/v1/chat/conversations` | Admin or Employee |
6061
| `GET` | `/v1/chat/conversations/{conversation_id}/messages` | Admin or Employee |
6162
| `GET` | `/healthz` | Public (liveness) |

app/api/v1/endpoints/chat.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Chat endpoint — submits a query to the LangGraph RAG agent."""
22

33
from fastapi import APIRouter, Depends, Header, Query
4+
from fastapi.responses import StreamingResponse
45
from sqlalchemy.ext.asyncio import AsyncSession
56

67
from app.api.dependencies import require_company_user
@@ -47,6 +48,46 @@ async def invoke_agent(
4748
return ChatResponse(response=response_text, conversation_id=conversation_id)
4849

4950

51+
@router.post(
52+
"/stream",
53+
responses={
54+
200: {
55+
"content": {
56+
"text/event-stream": {
57+
"example": 'event: token\\ndata: {"text":"Hello"}\\n\\nevent: done\\ndata: {"conversation_id":"...","cached":false}\\n\\n'
58+
}
59+
},
60+
"description": "Server-sent event stream of incremental chat tokens and completion metadata.",
61+
},
62+
403: {"description": "Forbidden — `super_admin` accounts cannot use chat."},
63+
429: {"description": "Too many chat requests."},
64+
},
65+
)
66+
async def stream_agent(
67+
request: ChatRequest,
68+
idempotency_key: str | None = Header(default=None, alias="X-Idempotency-Key"),
69+
db: AsyncSession = Depends(get_db),
70+
current_user: User = Depends(require_company_user),
71+
):
72+
"""Stream the chat response as SSE token events, then emit a final done event."""
73+
event_stream = await chat_service.create_streaming_response(
74+
query=request.query,
75+
conversation_id=request.conversation_id,
76+
idempotency_key=idempotency_key,
77+
db=db,
78+
current_user=current_user,
79+
)
80+
return StreamingResponse(
81+
event_stream,
82+
media_type="text/event-stream",
83+
headers={
84+
"Cache-Control": "no-cache",
85+
"Connection": "keep-alive",
86+
"X-Accel-Buffering": "no",
87+
},
88+
)
89+
90+
5091
@router.get("/conversations", response_model=list[ChatConversationResponse])
5192
async def list_conversations(
5293
limit: int | None = Query(default=None, ge=1, le=500),

0 commit comments

Comments
 (0)