-
Notifications
You must be signed in to change notification settings - Fork 0
Hook API Reference
Mosaic uses custom React hooks to encapsulate complex logic. This page documents every hook's parameters, return values, and behavior.
File: src/hooks/useStreamMessage.ts
Purpose: Core streaming hook. Sends a message to the AI provider, streams the response into a node, and triggers post-processing (confidence, tendrils, analytics).
function useStreamMessage(): {
sendMessage: (params: SendMessageParams) => Promise<void>;
stopStreaming: () => void;
isStreaming: boolean;
}interface SendMessageParams {
parentNodeId: string; // The node to reply to
text: string; // The user's message
model?: string; // Override the selected model
debate?: boolean; // If true, triggers parallel debate instead
}| Value | Type | Description |
|---|---|---|
sendMessage |
(params) => Promise<void> |
Sends a message and streams the response |
stopStreaming |
() => void |
Aborts the current streaming request |
isStreaming |
boolean |
True while a streaming response is in progress |
When sendMessage is called:
-
Create branch node: Adds a
branchnode for the user's message -
Build conversation path: Walks up parent edges via
getConversationPath()to build message history -
Check RAG context: If RAG is enabled, calls
ragStore.searchChunks()for top 3 chunks - Inject context: Appends RAG context to the system prompt
-
Send to provider: Calls
streamProvider()with the model, messages, and abort controller - Stream chunks: Each chunk updates the response node's label incrementally
-
Post-processing:
- Calls
useConfidenceScore(if enabled) to score the response - Calls
useSuggestionTendrils(if enabled) to generate follow-ups - Calls
analyticsStore.recordCompletion()to track usage
- Calls
-
Error handling: If streaming fails, sets
errorfield on the response node
const { sendMessage, stopStreaming, isStreaming } = useStreamMessage();
// Send a message
await sendMessage({ parentNodeId: 'node-1', text: 'Hello!' });
// Stop streaming mid-response
stopStreaming();
// Parallel debate
await sendMessage({ parentNodeId: 'node-1', text: 'Compare approaches', debate: true });
// Override model
await sendMessage({ parentNodeId: 'node-1', text: 'Explain', model: 'gpt-4o' });File: src/hooks/useConfidenceScore.ts
Purpose: After a response completes, asks the AI to self-score its response (0-100).
function useConfidenceScore(): {
scoreResponse: (nodeId: string, text: string, model: string) => Promise<void>;
}| Value | Type | Description |
|---|---|---|
scoreResponse |
(nodeId, text, model) => Promise<void> |
Sends calibration prompt and updates node with score |
Rate the confidence of your previous response on a scale of 0-100.
Consider: factual accuracy, clarity, completeness, and how well it addressed the user's question.
Respond with ONLY a number between 0 and 100.
- Timeout: 8 seconds (AbortController)
- Failure mode: Silent — no error thrown, node simply won't have a confidence badge
-
Updates: Sets the
confidencefield on the response node viaupdateNode()
const { scoreResponse } = useConfidenceScore();
await scoreResponse('node-5', 'The full response text...', 'mistral-large-latest');File: src/hooks/useSuggestionTendrils.ts
Purpose: Generates 3 follow-up suggestion nodes after an AI response.
function useSuggestionTendrils(): {
generateTendrils: (nodeId: string, conversationPath: string[], model: string) => Promise<void>;
}| Value | Type | Description |
|---|---|---|
generateTendrils |
(nodeId, conversationPath, model) => Promise<void> |
Generates 3 suggestions and spawns suggestion nodes |
Based on our conversation, suggest 3 follow-up questions the user might want to ask next.
Return them as a JSON array of strings: ["question1", "question2", "question3"]
- Creates 3
suggestiontype nodes connected viatendrilEdge - Sets a 30-second auto-dismiss timer (setTimeout)
- Each suggestion node has click handler for materialization
- If user clicks a suggestion, its text is copied to the parent node's input and the suggestion node is removed
const { generateTendrils } = useSuggestionTendrils();
await generateTendrils('node-5', ['msg1', 'msg2', 'msg3'], 'mistral-large-latest');File: src/hooks/useBranchPruning.ts
Purpose: Scores leaf branches for relevance to a user-provided goal and dims low-scoring branches.
function useBranchPruning(): {
pruneBranches: (goal: string) => Promise<void>;
clearPrune: () => void;
isPruning: boolean;
}| Value | Type | Description |
|---|---|---|
pruneBranches |
(goal) => Promise<void> |
Scores all branches against goal |
clearPrune |
() => void |
Restores all pruned branches |
isPruning |
boolean |
True while pruning is in progress |
- Collects all leaf nodes (nodes with no children)
- For each leaf, builds the conversation path
- Sends path content to AI with scoring prompt
- Parses numeric score from response (0-100)
- Branches with score < 40 get
pruned: true(configurable threshold) - Updates
pruneStorestate
const { pruneBranches, clearPrune, isPruning } = useBranchPruning();
await pruneBranches('I need to decide on a database for high-traffic web app');
clearPrune(); // Restore allFile: src/hooks/useDistillation.ts
Purpose: Summarizes all leaf branches into a single distillation node.
function useDistillation(): {
distill: () => Promise<void>;
isDistilling: boolean;
}| Value | Type | Description |
|---|---|---|
distill |
() => Promise<void> |
Collects leaves, sends for synthesis, creates distillation node |
isDistilling |
boolean |
True while distillation is in progress |
- Collects all leaf nodes (BFS traversal)
- For each leaf, retrieves conversation path via
getConversationPath() - Constructs a synthesis prompt with all leaf content
- Streams the AI response into a new
distillationtype node - Creates gold
distillEdgeconnections from distillation node to each source leaf - Positions distillation node at the bottom-center of the viewport
const { distill, isDistilling } = useDistillation();
await distill();File: src/hooks/useParallelDebate.ts
Purpose: Sends the same input to multiple models simultaneously and displays all responses.
function useParallelDebate(): {
debate: (params: DebateParams) => Promise<void>;
isDebating: boolean;
}interface DebateParams {
parentNodeId: string;
text: string;
models: string[]; // Models to use (from uiStore.debateModels)
}| Value | Type | Description |
|---|---|---|
debate |
(params) => Promise<void> |
Fans out requests to all models |
isDebating |
boolean |
True while debate is in progress |
- Creates one
branchnode for the user message - Fans out
streamProvider()calls viaPromise.allSettled() - Each model's response streams into its own
responsenode - Response nodes are positioned in a fan layout (angular spread)
- Failed models don't affect successful ones (allSettled)
const { debate, isDebating } = useParallelDebate();
await debate({
parentNodeId: 'node-1',
text: 'Compare databases',
models: ['mistral-large-latest', 'gpt-4o', 'claude-sonnet-4-20250514']
});File: src/hooks/useDocumentParser.ts
Purpose: Parses uploaded text/code files into chunks for RAG.
function useDocumentParser(): {
parseDocument: (file: File) => Promise<RagDocument | null>;
isParsing: boolean;
}| Value | Type | Description |
|---|---|---|
parseDocument |
(file) => Promise<RagDocument | null> |
Reads file, chunks text, returns RagDocument |
isParsing |
boolean |
True while parsing |
const CHUNK_SIZE = 800; // Characters per chunk
const CHUNK_OVERLAP = 100; // Character overlap between chunks- Read file as text (FileReader)
- Split text into chunks of CHUNK_SIZE with CHUNK_OVERLAP overlap
- Each chunk gets a unique ID and metadata (documentId, chunkIndex)
- Returns RagDocument with all chunks
25+ file extensions: .txt, .md, .json, .csv, .py, .js, .ts, .jsx, .tsx, .rs, .go, .java, .c, .cpp, .h, .hpp, .css, .html, .xml, .yaml, .yml, .toml, .pdf
const { parseDocument, isParsing } = useDocumentParser();
const doc = await parseDocument(fileInput.files[0]);
if (doc) {
ragStore.getState().addDocument(doc);
}File: src/hooks/useOllamaDetect.ts
Purpose: Detects Ollama connection and lists available models.
function useOllamaDetect(): {
detect: () => Promise<void>;
isDetecting: boolean;
isConnected: boolean;
models: string[];
}| Value | Type | Description |
|---|---|---|
detect |
() => Promise<void> |
Checks connection, updates store |
isDetecting |
boolean |
True during detection |
isConnected |
boolean |
True if Ollama is reachable |
models |
string[] |
Available Ollama models |
- Contacts Ollama API at
uiStore.ollamaUrl(defaulthttp://localhost:11434) - If reachable, fetches model list
- Updates
uiStore.ollamaConnectedanduiStore.ollamaModels - Runs on component mount (Settings drawer)
const { detect, isDetecting, isConnected, models } = useOllamaDetect();
useEffect(() => { detect(); }, []);File: src/hooks/useSpeechRecognition.ts
Purpose: Wraps the Web Speech API for voice input.
function useSpeechRecognition(): {
transcript: string;
isListening: boolean;
error: string | null;
startListening: () => void;
stopListening: () => void;
supported: boolean;
}| Value | Type | Description |
|---|---|---|
transcript |
string |
Current speech transcription |
isListening |
boolean |
True while microphone is active |
error |
string | null |
Error message (if any) |
startListening |
() => void |
Start speech recognition |
stopListening |
() => void |
Stop speech recognition |
supported |
boolean |
True if browser supports Web Speech API |
const { transcript, isListening, startListening, stopListening, supported } = useSpeechRecognition();
return (
<div>
<button onClick={startListening} disabled={!supported || isListening}>
🎤 {isListening ? 'Listening...' : 'Start'}
</button>
<button onClick={stopListening}>Stop</button>
<p>Transcript: {transcript}</p>
</div>
);Understanding when hooks fire and what side effects they trigger is important for debugging and extending Mosaic.
1. User presses Enter
2. sendMessage({ parentNodeId, text }) called
3. → Create branch node (side effect: canvasStore mutation)
4. → Set isStreaming = true
5. → Build conversation path (getConversationPath up the tree)
6. → If RAG enabled: searchChunks(query) (side effect: API call)
7. → Send to streamProvider(model, messages, config)
8. → For each chunk: updateNode(nodeId, { label: label + chunk })
9. → (side effect: canvasStore mutation → React Flow re-render)
10. → Stream complete
11. → Set isStreaming = false
12. → Parallel side effects (Promise.all):
13. → useConfidenceScore.scoreResponse() (if enabled)
14. → useSuggestionTendrils.generateTendrils() (if enabled)
15. → analyticsStore.recordCompletion()
16. → Debounced save to canvasManagerStore
| Hook | Error State | Recovery |
|---|---|---|
| useStreamMessage | Node shows error message + retry button | Click retry → re-sends message |
| useConfidenceScore | No badge shown (silent) | Next response re-attempts |
| useSuggestionTendrils | No suggestion nodes created (silent) | Next response re-attempts |
| useBranchPruning | Partial results (some branches scored) | Retry pruning |
| useDistillation | No distillation node created | Retry distillation |
| useParallelDebate | Failed models show error; successful models shown | Retry individual failed branches |
| useDocumentParser | File rejected with error message | Fix file and re-upload |
| useOllamaDetect | Ollama option grayed out | Click "Detect" to retry |
| useSpeechRecognition | Error message shown | Click mic again |
| Hook | Computation Cost | Network Cost | When to Optimize |
|---|---|---|---|
| useStreamMessage | Low (concatenation) | High (full streaming) | Large conversation history |
| useConfidenceScore | Low | Medium (one completion) | High-volume use (disable if not needed) |
| useSuggestionTendrils | Low | Medium (one completion) | High-volume use (disable if not needed) |
| useBranchPruning | Low | High (one completion per leaf) | Large canvases (many leaves) |
| useDistillation | Low | High (large context) | Very large canvases |
| useParallelDebate | Low | Very High (N completions) | Many models selected |
| useDocumentParser | Medium (chunking) | None | Large files (many chunks) |
useStreamMessage
├── useConfidenceScore (called after stream completes)
├── useSuggestionTendrils (called after stream completes)
└── analyticsStore.recordCompletion (called after stream completes)
useParallelDebate
└── useStreamMessage (multiple parallel calls)
useBranchPruning
└── streamProvider (for each leaf branch)
useDistillation
└── streamProvider (for synthesis)
- Store API Reference — All stores with state and actions
- Component API Reference — All components with props
- Utilities and Types Reference — Utility functions and type definitions
Mosaic — Branch, explore, and run code inline — an infinite canvas for AI conversations.
Built with Tauri, React, and Mistral AI.
GitHub Repository |
Report an Issue |
Releases
- Canvas and Node System
- LLM Provider Integration
- RAG System Guide
- Advanced AI Features
- Keyboard Shortcuts and UI Reference
- Tutorial - Branching and Parallel Exploration
- Tutorial - Using RAG with Documents
- Tutorial - Advanced Features in Practice
- Tutorial - Customizing Mosaic