Download frontend/src/hooks/useStreamingChat.js from Rixhot/ZARAI: direct link, hf CLI and curl.
- Browser
- Download file 3.98 kB
-
https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/frontend/src/hooks/useStreamingChat.js
- Command line
-
hf download hf://spaces/Rixhot/ZARAI/frontend/src/hooks/useStreamingChat.js
-
curl -L -o useStreamingChat.js https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/frontend/src/hooks/useStreamingChat.js
3.98 kB
| /** | |
| * JARVIS β useStreamingChat hook | |
| * Connects to the WebSocket streaming endpoint and manages | |
| * real-time token delivery + tool use events. | |
| * | |
| * Usage: | |
| * const { sendMessage, isStreaming, currentTokens, toolEvents } = useStreamingChat(sessionId) | |
| */ | |
| import { useState, useRef, useCallback, useEffect } from 'react' | |
| const WS_BASE = import.meta.env.VITE_WS_URL || | |
| (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + | |
| window.location.host + '/api/ws' | |
| export function useStreamingChat(sessionId) { | |
| const [isStreaming, setIsStreaming] = useState(false) | |
| const [currentTokens, setCurrentTokens] = useState('') // accumulates during stream | |
| const [toolEvents, setToolEvents] = useState([]) | |
| const [error, setError] = useState(null) | |
| const ws = useRef(null) | |
| const onDoneRef = useRef(null) | |
| // ββ Connect / reconnect βββββββββββββββββββββββββββββββββ | |
| const connect = useCallback(() => { | |
| if (!sessionId) return | |
| if (ws.current?.readyState === WebSocket.OPEN) return | |
| const url = `${WS_BASE}/chat/${sessionId}` | |
| ws.current = new WebSocket(url) | |
| ws.current.onopen = () => { | |
| setError(null) | |
| } | |
| ws.current.onmessage = (event) => { | |
| try { | |
| const msg = JSON.parse(event.data) | |
| switch (msg.type) { | |
| case 'thinking': | |
| setCurrentTokens('...') | |
| break | |
| case 'token': | |
| setCurrentTokens((prev) => prev === '...' ? msg.data : prev + msg.data) | |
| break | |
| case 'tool_use': | |
| setToolEvents((prev) => [...prev, msg.data]) | |
| break | |
| case 'done': | |
| setIsStreaming(false) | |
| if (onDoneRef.current) { | |
| onDoneRef.current({ | |
| text: currentTokens, | |
| used_tools: msg.data.used_tools || [], | |
| memory_hits: msg.data.memory_hits || 0, | |
| }) | |
| } | |
| setCurrentTokens('') | |
| setToolEvents([]) | |
| break | |
| case 'error': | |
| setError(msg.data) | |
| setIsStreaming(false) | |
| setCurrentTokens('') | |
| break | |
| default: | |
| break | |
| } | |
| } catch (e) { | |
| console.error('WS parse error:', e) | |
| } | |
| } | |
| ws.current.onerror = () => { | |
| setError('WebSocket connection error') | |
| setIsStreaming(false) | |
| } | |
| ws.current.onclose = () => { | |
| // Auto-reconnect after 2s if not intentionally closed | |
| setTimeout(() => { | |
| if (sessionId) connect() | |
| }, 2000) | |
| } | |
| }, [sessionId]) | |
| // ββ Connect on mount / sessionId change βββββββββββββββββ | |
| useEffect(() => { | |
| connect() | |
| return () => { | |
| ws.current?.close() | |
| } | |
| }, [connect]) | |
| // ββ Send message βββββββββββββββββββββββββββββββββββββββββ | |
| const sendMessage = useCallback( | |
| (message, imageCaption = null, onDone = null) => { | |
| if (!ws.current || ws.current.readyState !== WebSocket.OPEN) { | |
| connect() | |
| // Retry after connection | |
| setTimeout(() => sendMessage(message, imageCaption, onDone), 500) | |
| return | |
| } | |
| setIsStreaming(true) | |
| setCurrentTokens('') | |
| setToolEvents([]) | |
| setError(null) | |
| onDoneRef.current = onDone | |
| ws.current.send(JSON.stringify({ | |
| message, | |
| image_caption: imageCaption, | |
| })) | |
| }, | |
| [connect], | |
| ) | |
| // ββ Disconnect βββββββββββββββββββββββββββββββββββββββββββ | |
| const disconnect = useCallback(() => { | |
| ws.current?.close() | |
| }, []) | |
| return { | |
| sendMessage, | |
| disconnect, | |
| isStreaming, | |
| currentTokens, | |
| toolEvents, | |
| error, | |
| isConnected: ws.current?.readyState === WebSocket.OPEN, | |
| } | |
| } | |