WebSocket API Reference
Connection
Section titled “Connection”Endpoint
Section titled “Endpoint”ws[s]://{host}/ws/chat?session_id={uuid}&token={jwt}| Parameter | Type | Description |
|---|---|---|
session_id | UUID | The session to operate on |
token | string | Supabase JWT access token |
Protocol
Section titled “Protocol”- Transport: WebSocket (RFC 6455)
- Encoding: JSON text frames
- Authentication: JWT validated on connection upgrade
- Multiplexing: All chats in a session share one connection, distinguished by
chat_id
Connection URL construction
Section titled “Connection URL construction”const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';const host = window.location.host;const wsUrl = `${protocol}//${host}/ws/chat?session_id=${sessionId}&token=${accessToken}`;Client messages
Section titled “Client messages”Messages sent from the frontend to the backend.
Send a message to a single chat and stream the response.
{ "type": "query", "chat_id": "550e8400-e29b-41d4-a716-446655440000", "content": "Explain quantum computing"}| Field | Type | Description |
|---|---|---|
type | "query" | Message type discriminator |
chat_id | UUID string | Target chat |
content | string | User message text |
Backend behavior:
- Verifies the chat belongs to the authenticated user
- Inserts the user message into the
messagestable - Loads the full conversation history
- Calls
provider.send_stream()with the history - Sends
tokenmessages as tokens arrive - Inserts the assistant message and sends
done
query_all
Section titled “query_all”Send the same message to multiple chats simultaneously.
{ "type": "query_all", "content": "Explain quantum computing", "chat_ids": [ "550e8400-e29b-41d4-a716-446655440000", "550e8400-e29b-41d4-a716-446655440001", "550e8400-e29b-41d4-a716-446655440002" ]}| Field | Type | Description |
|---|---|---|
type | "query_all" | Message type discriminator |
content | string | User message text (sent to all chats) |
chat_ids | UUID string[] | Target chats |
Backend behavior:
Spawns a separate tokio::spawn task per chat_id. Each task executes the same flow as a single query. Responses stream concurrently and are interleaved on the same WebSocket connection, tagged with chat_id.
Server messages
Section titled “Server messages”Messages sent from the backend to the frontend.
A single token (or token chunk) from the LLM provider’s streaming response.
{ "type": "token", "chat_id": "550e8400-e29b-41d4-a716-446655440000", "content": "Quantum"}| Field | Type | Description |
|---|---|---|
type | "token" | Message type discriminator |
chat_id | UUID string | Which chat this token belongs to |
content | string | Token text (may be one or more characters) |
Signals that streaming is complete for a chat. Includes the full assembled response.
{ "type": "done", "chat_id": "550e8400-e29b-41d4-a716-446655440000", "full_content": "Quantum computing is a type of computation..."}| Field | Type | Description |
|---|---|---|
type | "done" | Message type discriminator |
chat_id | UUID string | Which chat completed |
full_content | string | Complete assistant response |
An error occurred during processing.
{ "type": "error", "chat_id": "550e8400-e29b-41d4-a716-446655440000", "message": "Model 'GPT-4' not available"}| Field | Type | Description |
|---|---|---|
type | "error" | Message type discriminator |
chat_id | UUID string or null | Which chat the error relates to (null for connection-level errors) |
message | string | Human-readable error description |
Common errors:
| Message | Cause |
|---|---|
"Unauthorized" | Invalid or expired JWT token |
"Chat not found" | Chat ID does not exist or is not owned by the user |
"Model 'X' not available" | Model is not in the plugin registry |
"Stream error: ..." | LLM provider returned an error during streaming |
"Invalid message: ..." | Client sent malformed JSON |
Message flow examples
Section titled “Message flow examples”Single chat query
Section titled “Single chat query”sequenceDiagram participant C as Client participant S as Server
C->>S: Connect ws://host/ws/chat?session_id=...&token=... Note over S: Validate JWT S-->>C: Connection established
C->>S: {"type":"query","chat_id":"abc","content":"Hello"}
S-->>C: {"type":"token","chat_id":"abc","content":"Hi"} S-->>C: {"type":"token","chat_id":"abc","content":" there"} S-->>C: {"type":"token","chat_id":"abc","content":"!"} S-->>C: {"type":"done","chat_id":"abc","full_content":"Hi there!"}Multi-chat query
Section titled “Multi-chat query”sequenceDiagram participant C as Client participant S as Server
C->>S: {"type":"query_all","content":"Hello","chat_ids":["a","b"]}
Note over S: Spawn task for chat "a" (GPT-4) Note over S: Spawn task for chat "b" (Mistral)
S-->>C: {"type":"token","chat_id":"a","content":"Hi"} S-->>C: {"type":"token","chat_id":"b","content":"Hello"} S-->>C: {"type":"token","chat_id":"a","content":" there"} S-->>C: {"type":"token","chat_id":"b","content":" there"} S-->>C: {"type":"done","chat_id":"b","full_content":"Hello there"} S-->>C: {"type":"token","chat_id":"a","content":"!"} S-->>C: {"type":"done","chat_id":"a","full_content":"Hi there!"}Tokens from different chats are interleaved. The client demultiplexes using the chat_id field.
Error during streaming
Section titled “Error during streaming”sequenceDiagram participant C as Client participant S as Server
C->>S: {"type":"query","chat_id":"abc","content":"Hello"}
S-->>C: {"type":"token","chat_id":"abc","content":"Hi"} S-->>C: {"type":"error","chat_id":"abc","message":"Stream error: connection reset"}When an error occurs mid-stream, the partial response is not saved to the database.
Reconnection
Section titled “Reconnection”The frontend WebSocket client automatically reconnects after 3 seconds when the connection drops:
- The
connectedstore is set tofalse - Pending streaming state is preserved in Svelte stores
- A new connection is established with the same session and token
Rate limiting
Section titled “Rate limiting”There is no built-in rate limiting on the WebSocket endpoint. In production, consider implementing rate limiting at the nginx reverse proxy level or in the application.