diff --git a/backend/app/routes/chat.py b/backend/app/routes/chat.py index 8957b455..584d9b29 100644 --- a/backend/app/routes/chat.py +++ b/backend/app/routes/chat.py @@ -774,18 +774,19 @@ def event_stream(): top_k=payload.top_k, chat_history=chat_history, ): - yield chunk - # Parse to accumulate full answer for history try: if chunk.startswith("data: "): data = json.loads(chunk[6:].strip()) + if data.get("type") == "done": + continue # We will yield our own done event with response time if data.get("type") == "token": full_answer += data.get("data", "") elif data.get("type") == "sources": sources = data.get("data", []) except Exception: pass + yield chunk # Cache the full answer for future identical questions if full_answer: @@ -811,6 +812,9 @@ def event_stream(): query_text_var.set(payload.question) chunks_retrieved_var.set(chunks_count) logger.info(f"Streaming RAG chat query completed, retrieved {chunks_count} chunks") + + elapsed_ms = round((time.perf_counter() - started_at) * 1000) + yield f"data: {json.dumps({'type': 'done', 'response_time_ms': elapsed_ms})}\n\n" finally: record_query_response_time(time.perf_counter() - started_at) diff --git a/frontend/src/components/chat/ChatPanel.tsx b/frontend/src/components/chat/ChatPanel.tsx index 927bcb4a..ddd30963 100644 --- a/frontend/src/components/chat/ChatPanel.tsx +++ b/frontend/src/components/chat/ChatPanel.tsx @@ -353,7 +353,7 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) { } else if (event.type === "done") { setMessages((prev) => prev.map((m) => - m.id === assistantId ? { ...m, isStreaming: false } : m, + m.id === assistantId ? { ...m, isStreaming: false, response_time_ms: event.response_time_ms } : m, ), ); ws.close(); @@ -449,7 +449,9 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) { } else if (event.type === "done") { setMessages((prev) => prev.map((m) => - m.id === assistantId ? { ...m, isStreaming: false } : m, + m.id === assistantId + ? { ...m, isStreaming: false, response_time_ms: (event as { type: string; response_time_ms?: number }).response_time_ms } + : m, ), ); } diff --git a/frontend/src/components/chat/MessageBubble.tsx b/frontend/src/components/chat/MessageBubble.tsx index ece6302a..a987b7d6 100644 --- a/frontend/src/components/chat/MessageBubble.tsx +++ b/frontend/src/components/chat/MessageBubble.tsx @@ -347,18 +347,25 @@ export default function MessageBubble({ message }: Props) { )} + > )}