Overview
The useChat hook is the core state management hook for PolyChat-AI, built with Zustand. It manages chat sessions, message streaming, multi-model support (up to 3 models simultaneously), and conversation templates.
Import
import { useChat } from '@/hooks/useChat';
Usage
const {
activeSessions,
currentSessionId,
selectedModels,
isAnyLoading,
sendMessageToAll,
createNewSession,
deleteMessage,
regenerateMessage,
stopStreaming,
} = useChat();
State Properties
Array of currently active chat sessions (max 3). Each session represents a conversation with a specific AI model.
All saved chat sessions, including inactive ones. Used for session history and switching between conversations.
ID of the currently active/focused session. Used for single-window mode.
Array of currently selected model IDs (max 3). Corresponds to the models in activeSessions.
True if any session is currently loading or streaming a response.
abortControllers
Record<string, AbortController>
Map of session IDs to AbortControllers for canceling streaming responses.
streamingProgress
Record<string, { chars: number; start: number; lastUpdate: number }>
Tracks streaming progress for each session, including character count and timing metrics.
pendingTemplate
ConversationTemplate | null
Template waiting to be applied to the conversation.
Whether the chat store has been initialized with saved data.
Core Methods
sendMessageToAll
Sends a message to all active sessions simultaneously.
await sendMessageToAll(content: string): Promise<void>
The message content to send to all active models.
Features:
- Validates content before sending
- Filters out pending sessions (not yet assigned a model)
- Handles image generation detection and routing
- Integrates with RAG (Retrieval-Augmented Generation) when enabled
- Applies system prompts and tone settings
- Streams responses in real-time
- Sends notifications when complete
Example:
const { sendMessageToAll, activeSessions } = useChat();
// Send a message to all active models
await sendMessageToAll('Explain quantum computing in simple terms');
// Automatically detects image generation requests
await sendMessageToAll('Génère une image d\'un coucher de soleil');
regenerateMessage
Regenerates a specific assistant message.
await regenerateMessage(sessionId: string, messageId: string): Promise<void>
ID of the session containing the message.
ID of the assistant message to regenerate.
Example:
const { regenerateMessage } = useChat();
// Regenerate a specific response
await regenerateMessage('session-123', 'message-456');
deleteMessage
Deletes a message from a session.
deleteMessage(sessionId: string, messageId: string): void
ID of the session containing the message.
ID of the message to delete.
stopStreaming
Stops streaming for one or all sessions.
stopStreaming(sessionId?: string): void
Optional session ID. If not provided, stops streaming for all sessions.
Example:
const { stopStreaming, activeSessions } = useChat();
// Stop a specific session
stopStreaming('session-123');
// Stop all streaming
stopStreaming();
Session Management
initializeChat
Initializes the chat store, loading saved sessions and creating a temporary session.
Called automatically on app load. Loads chat history from localStorage and creates a new temporary session.
createNewSession
Creates a new chat session with the currently selected model.
deleteSession
Deletes a session from history.
deleteSession(sessionId: string): void
ID of the session to delete.
setActiveSession
Switches to a different saved session.
setActiveSession(sessionId: string): void
ID of the session to activate.
clearAllChats
Clears all messages from the current session.
Multi-Model Management
addModel
Adds a new model to active sessions (max 3).
addModel(modelId: string): void
ID of the model to add (e.g., ‘anthropic/claude-3.5-sonnet’).
Example:
const { addModel, selectedModels } = useChat();
if (selectedModels.length < 3) {
addModel('openai/gpt-4-turbo');
}
removeModel
Removes a model from active sessions.
removeModel(modelId: string): void
ID of the model to remove.
setWindowCount
Sets the number of active chat windows (1-3).
setWindowCount(count: number): void
Number of windows (clamped to 1-3).
setSessionModel
Assigns a model to a pending session.
setSessionModel(sessionId: string, modelId: string): void
ID of the pending session.
ID of the model to assign.
Template & Quick Actions
applyTemplate
Applies a conversation template to all active sessions.
applyTemplate(template: ConversationTemplate): void
template
ConversationTemplate
required
Template to apply, containing systemPrompt and userMessage.
Example:
const template: ConversationTemplate = {
id: 'code-review',
name: 'Code Review',
category: 'programming',
description: 'Review code for quality and bugs',
systemPrompt: 'You are an expert code reviewer...',
userMessage: 'Please review this code: {code}',
tags: ['code', 'review'],
isCustom: false,
};
applyTemplate(template);
prepareTemplate
Prepares a template for editing before application.
prepareTemplate(template: ConversationTemplate | null): void
template
ConversationTemplate | null
required
Template to prepare, or null to clear.
executeQuickAction
Executes a quick action on selected text.
executeQuickAction(action: QuickAction, selectedText?: string): void
Quick action to execute (explain, optimize, debug, etc.).
Text to apply the action to (required if action.requiresSelection is true).
Example:
const action: QuickAction = {
id: 'explain',
name: 'Explain',
icon: '📖',
action: 'explain',
description: 'Explain selected code',
requiresSelection: true,
systemPrompt: 'You are a helpful programming tutor.',
userMessageTemplate: 'Explain this code: {selectedText}',
};
executeQuickAction(action, 'const x = 5;');
Helper Methods
createNewTemporarySession
Creates a new temporary session (not saved until first message).
createNewTemporarySession(modelId?: string): ChatSession
Model ID for the session. Defaults to ‘default’ if not provided.
Types
ChatSession
interface ChatSession {
id: string;
modelId: string;
modelName: string;
messages: Message[];
isLoading: boolean;
error: string | null;
isTemporary?: boolean;
}
Message
interface Message {
id: string;
role: 'user' | 'assistant' | 'system';
content: string | MessageContent[];
timestamp: Date;
modelId?: string;
streaming?: boolean;
imageData?: GeneratedImage;
}
MessageContent
interface MessageContent {
type: 'text' | 'image_url';
text?: string;
image_url?: {
url: string;
detail?: 'low' | 'high' | 'auto';
};
}
Features
Image Generation Detection
The hook automatically detects image generation requests in multiple languages (English, French) using keyword matching:
const keywords = ['génér', 'créer', 'dessin', 'image', 'photo', 'illustration', ...];
When detected with a compatible model, routes to image generation instead of text streaming.
RAG Integration
When RAG is enabled in settings, the hook automatically retrieves relevant context from conversation history:
if (ragEnabled) {
const relevantHistory = await getRelevantContext(content, sessionMessages);
contextMessages = [...relevantHistory, lastUserMessage];
}
Streaming Progress Tracking
Tracks real-time streaming metrics for each session:
streamingProgress: {
'session-123': {
chars: 1024,
start: 1234567890,
lastUpdate: 1234567900
}
}
Auto-Save
Sessions are automatically saved to localStorage whenever the state changes, excluding empty sessions.
Example: Complete Chat Flow
import { useChat } from '@/hooks/useChat';
import { useSettings } from '@/hooks/useSettings';
function ChatComponent() {
const {
activeSessions,
selectedModels,
isAnyLoading,
sendMessageToAll,
addModel,
stopStreaming,
} = useChat();
const { apiKey } = useSettings();
const handleSend = async (message: string) => {
if (!apiKey) {
console.error('API key required');
return;
}
await sendMessageToAll(message);
};
const handleAddModel = () => {
if (selectedModels.length < 3) {
addModel('anthropic/claude-3.5-sonnet');
}
};
return (
<div>
{activeSessions.map((session) => (
<div key={session.id}>
<h3>{session.modelName}</h3>
{session.messages.map((msg) => (
<div key={msg.id}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
</div>
))}
<button onClick={handleAddModel} disabled={selectedModels.length >= 3}>
Add Model
</button>
{isAnyLoading && (
<button onClick={() => stopStreaming()}>Stop</button>
)}
</div>
);
}