|
1 | 1 | """Chat endpoint — submits a query to the LangGraph RAG agent.""" |
2 | 2 |
|
3 | 3 | from fastapi import APIRouter, Depends, Header, Query |
| 4 | +from fastapi.responses import StreamingResponse |
4 | 5 | from sqlalchemy.ext.asyncio import AsyncSession |
5 | 6 |
|
6 | 7 | from app.api.dependencies import require_company_user |
@@ -47,6 +48,46 @@ async def invoke_agent( |
47 | 48 | return ChatResponse(response=response_text, conversation_id=conversation_id) |
48 | 49 |
|
49 | 50 |
|
| 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 | + |
50 | 91 | @router.get("/conversations", response_model=list[ChatConversationResponse]) |
51 | 92 | async def list_conversations( |
52 | 93 | limit: int | None = Query(default=None, ge=1, le=500), |
|
0 commit comments