Files
reflector/.flow/specs/fn-1.5.md
Igor Loskutov 316f7b316d feat: add WebVTT context generation to chat WebSocket endpoint
- Import topics_to_webvtt_named and recordings controller
- Add _get_is_multitrack helper function
- Generate WebVTT context on WebSocket connection
- Add get_context message type to retrieve WebVTT
- Maintain backward compatibility with echo for other messages
- Add test fixture and test for WebVTT context generation

Implements task fn-1.2: WebVTT context generation for transcript chat
2026-01-12 18:24:47 -05:00

2.5 KiB

Task 5: Frontend WebSocket Hook

File: www/app/(app)/transcripts/useTranscriptChat.ts Lines: ~60 Dependencies: Task 1 (protocol defined)

Objective

Create React hook for WebSocket chat communication.

Implementation

import { useEffect, useState, useRef } from "react"
import { WEBSOCKET_URL } from "../../lib/apiClient"

type Message = {
  id: string
  role: "user" | "assistant"
  text: string
  timestamp: Date
}

export const useTranscriptChat = (transcriptId: string) => {
  const [messages, setMessages] = useState<Message[]>([])
  const [isStreaming, setIsStreaming] = useState(false)
  const [currentStreamingText, setCurrentStreamingText] = useState("")
  const wsRef = useRef<WebSocket | null>(null)

  useEffect(() => {
    const ws = new WebSocket(
      `${WEBSOCKET_URL}/v1/transcripts/${transcriptId}/chat`
    )
    wsRef.current = ws

    ws.onopen = () => console.log("Chat WebSocket connected")

    ws.onmessage = (event) => {
      const msg = JSON.parse(event.data)

      switch (msg.type) {
        case "token":
          setIsStreaming(true)
          setCurrentStreamingText((prev) => prev + msg.text)
          break

        case "done":
          setMessages((prev) => [
            ...prev,
            {
              id: Date.now().toString(),
              role: "assistant",
              text: currentStreamingText,
              timestamp: new Date(),
            },
          ])
          setCurrentStreamingText("")
          setIsStreaming(false)
          break

        case "error":
          console.error("Chat error:", msg.message)
          setIsStreaming(false)
          break
      }
    }

    ws.onerror = (error) => console.error("WebSocket error:", error)
    ws.onclose = () => console.log("Chat WebSocket closed")

    return () => ws.close()
  }, [transcriptId, currentStreamingText])

  const sendMessage = (text: string) => {
    if (!wsRef.current) return

    setMessages((prev) => [
      ...prev,
      {
        id: Date.now().toString(),
        role: "user",
        text,
        timestamp: new Date(),
      },
    ])

    wsRef.current.send(JSON.stringify({ type: "message", text }))
  }

  return { messages, sendMessage, isStreaming, currentStreamingText }
}

Validation

  • Hook connects to WebSocket
  • Sends messages to server
  • Receives streaming tokens
  • Accumulates tokens into messages
  • Handles done/error events
  • Closes connection on unmount

Notes

  • Test with browser console first
  • Verify message format matches backend protocol