diff --git a/backend/app/api/endpoints/ai_sessions.py b/backend/app/api/endpoints/ai_sessions.py index 537737e6..9e6fc943 100644 --- a/backend/app/api/endpoints/ai_sessions.py +++ b/backend/app/api/endpoints/ai_sessions.py @@ -924,6 +924,52 @@ async def get_documentation( raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e)) +@router.get("/{session_id}/documentation/stream") +@limiter.limit("20/minute") +async def stream_documentation( + request: Request, + session_id: UUID, + current_user: Annotated[User, Depends(get_current_active_user)], + db: Annotated[AsyncSession, Depends(get_db)], +): + """Stream AI-generated ticket notes as Server-Sent Events.""" + from starlette.responses import StreamingResponse + + # Verify session ownership + result = await db.execute( + select(AISession).where(AISession.id == session_id) + ) + session = result.scalar_one_or_none() + if not session: + raise HTTPException(status_code=404, detail="Session not found") + if session.user_id != current_user.id: + raise HTTPException(status_code=403, detail="Not authorized") + + async def event_generator(): + try: + async for chunk in flowpilot_engine.stream_ticket_notes( + session_id=session_id, + user_id=current_user.id, + db=db, + ): + # SSE format: data: \n\n + yield f"data: {chunk}\n\n" + yield "data: [DONE]\n\n" + except Exception as e: + logger.exception("SSE stream error for session %s: %s", session_id, e) + yield f"data: [ERROR] {str(e)}\n\n" + + return StreamingResponse( + event_generator(), + media_type="text/event-stream", + headers={ + "Cache-Control": "no-cache", + "Connection": "keep-alive", + "X-Accel-Buffering": "no", # Disable nginx buffering + }, + ) + + # ── Status Update ── @router.post("/{session_id}/status-update", response_model=StatusUpdateResponse)