diff --git a/surfsense_backend/app/services/streaming_service.py b/surfsense_backend/app/services/streaming_service.py index ab42c3b84..08b31b733 100644 --- a/surfsense_backend/app/services/streaming_service.py +++ b/surfsense_backend/app/services/streaming_service.py @@ -6,22 +6,10 @@ class StreamingService: def __init__(self): self.terminal_idx = 1 self.message_annotations = [ - { - "type": "TERMINAL_INFO", - "content": [] - }, - { - "type": "SOURCES", - "content": [] - }, - { - "type": "ANSWER", - "content": [] - }, - { - "type": "FURTHER_QUESTIONS", - "content": [] - } + {"type": "TERMINAL_INFO", "content": []}, + {"type": "SOURCES", "content": []}, + {"type": "ANSWER", "content": []}, + {"type": "FURTHER_QUESTIONS", "content": []}, ] # DEPRECATED: This sends the full annotation array every time (inefficient) @@ -35,7 +23,7 @@ class StreamingService: Returns: str: The formatted annotations string """ - return f'8:{json.dumps(self.message_annotations)}\n' + return f"8:{json.dumps(self.message_annotations)}\n" def format_terminal_info_delta(self, text: str, message_type: str = "info") -> str: """ @@ -55,7 +43,17 @@ class StreamingService: self.message_annotations[0]["content"].append(message) # Return only the delta annotation - annotation = {"type": "TERMINAL_INFO", "content": [message]} + # annotation = {"type": "TERMINAL_INFO", "content": [message]} + agent_data = { + "agent_name": "Terminal agent", + "events": [ + { + "status": message_type, + "result": message.get("text", ""), + } + ], + } + annotation = {"type": "agent_events", "data": agent_data} return f"8:[{json.dumps(annotation)}]\n" def format_sources_delta(self, sources: List[Dict[str, Any]]) -> str: @@ -155,14 +153,16 @@ class StreamingService: """ return f"3:{json.dumps(error_message)}\n" - def format_completion(self, prompt_tokens: int = 156, completion_tokens: int = 204) -> str: + def format_completion( + self, prompt_tokens: int = 156, completion_tokens: int = 204 + ) -> str: """ Format a completion message - + Args: prompt_tokens: Number of prompt tokens completion_tokens: Number of completion tokens - + Returns: str: The formatted completion string """ @@ -172,7 +172,7 @@ class StreamingService: "usage": { "promptTokens": prompt_tokens, "completionTokens": completion_tokens, - "totalTokens": total_tokens - } + "totalTokens": total_tokens, + }, } - return f'd:{json.dumps(completion_data)}\n' \ No newline at end of file + return f"d:{json.dumps(completion_data)}\n" diff --git a/surfsense_web/components/chat_v2/ChatInterface.tsx b/surfsense_web/components/chat_v2/ChatInterface.tsx index 92d23a993..be3a85dcf 100644 --- a/surfsense_web/components/chat_v2/ChatInterface.tsx +++ b/surfsense_web/components/chat_v2/ChatInterface.tsx @@ -5,11 +5,14 @@ import { ChatHandler, ChatCanvas, ChatMessages, + useChatUI, + ChatMessage, } from "@llamaindex/chat-ui"; import { Document } from "@/hooks/use-documents"; import { CustomChatInput } from "@/components/chat_v2/ChatInputGroup"; import { ResearchMode } from "@/components/chat"; import React from "react"; +import TerminalDisplay from "@/components/chat_v2/ChatTerminal"; interface ChatInterfaceProps { handler: ChatHandler; @@ -23,6 +26,30 @@ interface ChatInterfaceProps { onResearchModeChange?: (mode: ResearchMode) => void; } +function ChatMessageDisplay() { + const { messages } = useChatUI(); + + return ( + + + {messages.map((message, index) => ( +
+ {message.role === "assistant" && ( + + )} + +
+ ))} +
+ +
+ ); +} + export default function ChatInterface({ handler, onDocumentSelectionChange, @@ -37,13 +64,7 @@ export default function ChatInterface({ return (
- - - {/* Custom message rendering */} - - - - +
msg.role === "assistant") + .pop(); + + if (!lastAssistantMessage) { + return <>; + } + + const annotations = getAnnotationData(lastAssistantMessage, "agent_events"); + + if (annotations.length === 0) { + return <>; + } + + // Get all agent events and combine them + const events: any[] = []; + annotations.forEach((annotation: any) => { + if (annotation?.events && Array.isArray(annotation.events)) { + events.push(...annotation.events); + } + }); + + if (events.length === 0) { + return <>; + } + + return ( +
+ {/* Terminal Header */} +
setIsCollapsed(!isCollapsed)} + > +
+
+
+
+
+
+ Agent Process Terminal ({events.length} events) +
+
+ {isCollapsed ? ( + + + + ) : ( + + + + )} +
+
+ + {/* Terminal Content */} + {!isCollapsed && ( +
+ {events.map((event, index) => ( +
+ $ + + [{event.status || "completed"}] + + {event.result && ( + + {event.result.slice(0, 100)}... + + )} +
+ ))} + {events.length === 0 && ( +
+ No agent events to display... +
+ )} +
+ )} +
+ ); +}