Skip to content

Chat System Architecture

The Gnosari chat system provides real-time, streaming AI conversations with agents and teams. Built on WebSocket technology, it delivers instant responses with live typing indicators and seamless message delivery.

Overview

The chat system powers all conversational interfaces in Gnosari:

  • Public chat pages (/chat/[agent_identifier])
  • Agent chat pages (/gnosaris/[id]/chat)
  • Team chat pages (/teams/[id]/chat)
  • Session continuation (/sessions/[session_id]/chat)
  • Embedded widgets (/embed/agent/[identifier])

Core Features

FeatureDescription
Real-time streamingAI responses appear word-by-word as they're generated
WebSocket connectionPersistent connection for instant messaging
Message typesUser, Assistant, Function Call, Function Result, Reasoning
Session persistenceContinue conversations across page reloads
Public & authenticatedWorks for both logged-in users and anonymous visitors
Team routingIntelligent agent selection for team conversations

Chat Routes

The system provides multiple entry points for starting conversations:

1. Public Chat (/chat/[agent_identifier])

Chat with any public agent using their identifier:

URL: /chat/customer-support
URL: /chat/sales-assistant

Features:
- No login required for public agents
- Password prompt for protected agents
- Session creation on first message
- Chat interface without the owner conversation sidebar (session-history panel requires authentication)

Use Cases:

  • Public-facing chatbots on websites
  • Customer support bots
  • Sales assistants

2. Agent Chat (/chat/agent/[agent_identifier])

Alternative route specifically for agent chats:

URL: /chat/agent/customer-support

Features:
- Same as public chat
- Explicit "agent" route for clarity
- Useful for programmatic navigation

3. Authenticated Agent Chat (/gnosaris/[id]/chat)

Chat with any agent (public or private) when logged in:

URL: /gnosaris/123/chat

Features:
- Full authentication required
- Access to private agents
- Session history in sidebar
- New chat button

Use Cases:

  • Internal agent testing
  • Private agent conversations
  • Multi-session management

4. Team Chat (/teams/[id]/chat)

Chat with entire teams:

URL: /teams/456/chat

Features:
- All team agents available
- Agent sidebar for switching
- Manager/orchestrator routing
- Seamless agent handoffs

5. Session Continuation (/sessions/[session_id]/chat)

Continue existing conversations:

URL: /sessions/ui_1704067200000_abc123/chat

Features:
- Load full message history
- Pick up where you left off
- Session metadata visible
- Same interface as new chats

WebSocket Architecture

Real-time messaging is powered by WebSocket connections.

Connection Flow

1. Page loads → Chat component mounts
2. useChatGnosari() initializes
3. WebSocket connects to server
4. Server sends 'connected' event
5. Session created or loaded
6. User sends message
7. Server streams response in real-time
8. Connection maintained until page closes

Connection States

StateDescriptionUser Experience
ConnectingInitial connection attemptLoading indicator
ConnectedActive WebSocket connectionGreen status dot, can send messages
DisconnectedConnection lostRed banner, "Reconnect" button
ReconnectingAuto-retry in progressYellow spinner, exponential backoff

WebSocket Events

The system handles these event types:

Incoming Events (Server → Client)

Event TypeDescriptionTriggers
text_deltaStreaming text chunkUpdate message content in real-time
agent_completedAgent finished responseHide typing indicator, enable input
tool_call_startedTool invocation beginningShow tool activity badge
tool_call_progressTool executingUpdate progress indicator
tool_call_completedTool finishedDisplay result, hide badge
agent_thinkingAgent processingShow thinking indicator
errorError occurredDisplay error message banner

Outgoing Events (Client → Server)

Event TypeDescriptionPayload
user_messageUser sent message{ content: string, session_id: string }
select_agentSwitch agent (teams){ agent_id: number }
end_sessionClose conversation{ session_id: string }

Auto-Reconnect Logic

When connection drops:

typescript
Attempt 1: Wait 1 second → Try reconnect
Attempt 2: Wait 2 seconds → Try reconnect
Attempt 3: Wait 4 seconds → Try reconnect
Attempt 4: Wait 8 seconds → Try reconnect
Attempt 5: Wait 16 seconds → Try reconnect
Max wait: 30 seconds between retries

If all fail: Show manual "Reconnect" button

Session Preservation:

  • Messages saved to state during connection loss
  • No data lost during reconnect
  • Full history restored on successful reconnect

Message Rendering

The chat system displays five types of messages:

1. User Messages

Messages you send to the AI.

Appearance:

  • Right-aligned (your side of conversation)
  • Teal/emerald gradient background
  • White text
  • Timestamp when grouped messages change speaker

Example:

                    [How do I reset my password?]
                    [10:30 AM]

2. Assistant Messages

AI responses.

Appearance:

  • Left-aligned (AI side of conversation)
  • Indigo/blue gradient background
  • Markdown rendering for rich text
  • Agent name and avatar
  • Timestamp

Features:

  • Markdown support: Bold, italics, lists, code blocks, links
  • Code syntax highlighting: Language-specific coloring
  • Streaming: Text appears word-by-word as generated
  • Typing indicator: Shows when AI is composing

Example:

[🤖 Support Bot]
To reset your password:
1. Go to Settings
2. Click "Change Password"
3. Enter new password
4. Click Save

[10:30 AM]

3. Function Call Messages

When AI invokes a tool.

Appearance:

  • Purple badge with tool icon
  • Shows tool name (human-readable)
  • Collapsible to show parameters
  • Technical details hidden by default

Example:

🔧 Searching Knowledge Base
   ▼ Show details
   Query: "password reset"
   Source: "Support Docs"
   Max Results: 5

Toggle Visibility:

Use the "Tools" toggle in chat header to show/hide function call messages.

4. Function Result Messages

Output from tool executions.

Appearance:

  • Gray background
  • JSON-formatted data
  • Collapsible for long results
  • Shows execution time

Example:

📊 Knowledge Base Results
   Found 3 relevant documents:
   1. "How to Reset Password"
   2. "Account Security Guide"
   3. "Password Requirements"

   Execution time: 245ms

5. Reasoning Messages

Agent's internal thinking process (when enabled).

Appearance:

  • Light background
  • Italic text
  • Collapsible section
  • Shows step-by-step reasoning

Example:

💭 Agent Thinking
   ▼ Show reasoning
   1. User asks about password reset
   2. I should search our knowledge base
   3. Tool call: search_knowledge_base
   4. Results found, I'll summarize the steps
   5. Provide clear, numbered instructions

When Enabled:

Reasoning messages appear when:

  • Agent has "Advanced Reasoning" capability enabled
  • Chat theme has reasoning display enabled

Chat Composables

The system uses specialized Vue composables for different concerns:

Core Composable: useChatGnosari()

Location: ~/composables/features/useChatGnosari.ts

Purpose: Main chat state and WebSocket management

Provides:

PropertyTypeDescription
stateReadonly<ChatState>Reactive chat state (session, messages, agents)
messagesComputedRef<ParsedMessage[]>Parsed message list with metadata
hasMessagesComputedRef<boolean>Whether conversation has started
canSendMessageComputedRef<boolean>Whether input is enabled

Actions:

MethodParametersDescription
sendMessage()content: stringSend user message
selectTeam()team: TeamSwitch to team chat
selectAgent()agent: ChatAgent | nullSwitch agent in team chat
loadSession()sessionId: stringLoad existing session
clearChat()-Reset conversation
manualReconnect()-Force WebSocket reconnect
cleanup()-Disconnect and cleanup

Usage:

vue
<script setup lang="ts">
const chat = useChatGnosari()

// Send message
const handleSendMessage = async (message: string) => {
  try {
    await chat.sendMessage(message)
  } catch (error) {
    console.error('Failed to send:', error)
  }
}

// Check connection
const isConnected = computed(() => chat.state.isConnected)

// Access messages
const messageList = computed(() => chat.messages.value)
</script>

Supporting Composables

useChatActions()

Purpose: User action handlers (copy, share, retry)

Provides:

  • copyToClipboard(): Copy conversation to clipboard
  • shareConversation(): Generate shareable link
  • retryLastMessage(): Resend failed message
  • clearConversation(): Reset with confirmation

useChatInitialization()

Purpose: Initial setup and configuration

Provides:

  • initializeChat(): Set up WebSocket and session
  • loadAgentConfig(): Fetch agent settings
  • applyTheme(): Apply chat theme configuration

useChatModals()

Purpose: Modal state management

Provides:

  • showPasswordModal: Password prompt state
  • showWelcomeModal: Welcome modal state
  • showContactModal: Contact form state
  • showSettingsModal: Settings modal state

useChatSidebar()

Purpose: Global chat sidebar state (for floating widget)

Provides:

  • isOpen: Sidebar visibility
  • open(): Open sidebar with agent
  • close(): Close sidebar
  • toggle(): Toggle open/close

Public vs Authenticated Chat

The system adapts to different user types:

Public Chat (Anonymous Users)

Features:

  • No login required
  • Password prompt for protected agents
  • Session stored in localStorage
  • Limited to public agents only
  • Basic chat interface

Limitations:

  • Cannot access private agents
  • No session history sidebar
  • Cannot view past conversations
  • Session lost if localStorage cleared
  • No owner conversation/session-history sidebar — the ChatSidebar panel is hidden for anonymous visitors regardless of agent preset

Authenticated Chat (Logged In)

Features:

  • Access to all agents (public & private)
  • Full session history
  • Session persistence across devices
  • Agent management capabilities
  • Advanced features (model selection, etc.)

Benefits:

  • Sessions saved to account
  • Continue on any device
  • View full conversation history
  • Create and manage agents

Message Parsing

Raw WebSocket events are parsed into structured messages.

ParsedMessage Interface

typescript
interface ParsedMessage {
  // Core fields
  sessionMessageId: number     // Unique message ID
  role: 'user' | 'assistant' | 'function_call' | 'function_call_output' | 'reasoning'
  displayContent: string        // Rendered content
  timestamp: Date               // When sent

  // Optional metadata
  agentName?: string            // Which agent responded
  toolName?: string             // Tool invoked (function calls)
  toolArguments?: any           // Tool parameters
  toolResult?: any              // Tool output
  messageId?: string            // WebSocket message ID
  callId?: string               // Tool call ID
}

Message Grouping

Messages are grouped by speaker for visual clarity:

User A: Message 1       ──┐
User A: Message 2         ├─ Grouped (same speaker)
User A: Message 3       ──┘

AI: Response            ──── New group (different speaker)

Rules:

  • Group consecutive messages from same speaker
  • Break group when speaker changes
  • Show timestamp only on group boundaries

Error Handling

The system gracefully handles errors:

Connection Errors

ErrorCauseUser Experience
WebSocket failedNetwork issueRed banner, auto-reconnect
Authentication failedInvalid token/passwordPassword prompt re-appears
Session not foundSession deletedError message, "New Chat" button
Agent unavailableAgent deleted/disabledError banner, cannot send messages

Message Errors

ErrorCauseUser Experience
Send failedNetwork timeoutMessage marked failed, retry button
Rate limitToo many messagesWarning banner, temporary disable
Content violationFiltered contentError message, prompt to rephrase

Recovery Actions

For connection errors:

1. Show error banner with description
2. Provide "Reconnect" button
3. Auto-retry with backoff
4. Preserve message queue

For message errors:

1. Mark message as failed
2. Keep message in input (user can edit)
3. Show retry button
4. Log error for debugging

Best Practices

Component Integration

DO:

vue
<script setup lang="ts">
const chat = useChatGnosari()

// Initialize on mount
onMounted(() => {
  chat.initialize()
})

// Cleanup on unmount
onUnmounted(() => {
  chat.cleanup()
})
</script>

DON'T:

vue
<script setup lang="ts">
// ❌ Don't create multiple chat instances
const chat1 = useChatGnosari()
const chat2 = useChatGnosari()

// ❌ Don't forget cleanup
// (no onUnmounted cleanup)
</script>

State Management

DO:

vue
<script setup lang="ts">
const chat = useChatGnosari()

// Use computed for derived state
const isEmpty = computed(() => !chat.hasMessages.value)
const canInteract = computed(() =>
  chat.state.isConnected && !chat.state.isLoading
)
</script>

DON'T:

vue
<script setup lang="ts">
// ❌ Don't mutate state directly
chat.state.messages.push(newMessage)

// ❌ Don't rely on synchronous updates
chat.sendMessage('Hello')
// messages not updated yet!
</script>

Error Handling

DO:

vue
<script setup lang="ts">
const handleSend = async (message: string) => {
  try {
    await chat.sendMessage(message)
  } catch (error) {
    // Show user-friendly error
    toast.add({
      title: 'Message failed',
      description: 'Please try again',
      color: 'error'
    })
  }
}
</script>

Performance Optimization

Message Rendering

Virtual Scrolling: For conversations with 100+ messages Lazy Loading: Load messages in chunks Memo-ization: Cache parsed message objects

WebSocket Optimization

Reconnect Debouncing: Prevent rapid reconnect attempts Message Batching: Group rapid messages when possible State Cleanup: Remove old messages from memory


What's Next?

  • Chat Themes: Customize visual appearance and behavior
  • Chat Sidebar: Implement global floating chat widget
  • Widget Embedding: Embed chat on external websites