spb/vquant Public MIT
VibeQuant — AI-powered institutional-grade financial intelligence platform.
TypeScript 84.3%
Python 11.7%
JavaScript 1.6%
CSS 1.5%
HTML 0.7%
1/*2 * =============================================================================3 * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 * File: client/src/hooks/useChatMutation.ts6 *7 * Author: Simon-Pierre Boucher8 * Contact: contact@spboucher.ai9 * Website: https://www.spboucher.ai10 * Demo: https://www.vquant.ai11 * License: MIT (see LICENSE)12 *13 * Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import { useMutation } from '@tanstack/react-query';18import type { SearchResult } from '@shared/types';1920interface ConversationMessage {21 question: string;22 answer: string;23}2425interface ChatRequest {26 query: string;27 history?: ConversationMessage[];28 sessionId?: string;29 imageData?: string;30 imageMimeType?: string;31 model?: string;32}3334interface ChatResponse {35 answer: string;36 sources: SearchResult[];37 sessionId: string;38 error?: string;39}4041async function streamChat(request: ChatRequest, onChunk: (data: any) => void): Promise<ChatResponse> {42 const response = await fetch('/api/chat', {43 method: 'POST',44 headers: {45 'Content-Type': 'application/json',46 },47 body: JSON.stringify(request),48 });4950 if (!response.ok) {51 throw new Error('Failed to connect to chat API');52 }5354 const reader = response.body?.getReader();55 const decoder = new TextDecoder();5657 if (!reader) {58 throw new Error('No response body');59 }6061 let answer = '';62 let sources: SearchResult[] = [];63 let sessionId = '';64 let buffer = '';6566 while (true) {67 const { done, value } = await reader.read();6869 if (done) break;7071 buffer += decoder.decode(value, { stream: true });72 const lines = buffer.split('\n');73 buffer = lines.pop() || '';7475 for (const line of lines) {76 if (line.startsWith('data: ')) {77 const data = line.substring(6);78 if (!data.trim()) continue;7980 try {81 const event = JSON.parse(data);8283 if (event.type === 'python_code') {84 console.log('useChatMutation: Received python_code event', event);85 }8687 onChunk(event);8889 if (event.type === 'text') {90 answer += event.content;91 } else if (event.type === 'sources') {92 sources = event.sources;93 } else if (event.type === 'done') {94 sessionId = event.sessionId;95 } else if (event.type === 'error') {96 throw new Error(event.error);97 }98 } catch (e) {99 if (e instanceof Error && e.message !== 'Unexpected end of JSON input') {100 throw e;101 }102 }103 }104 }105 }106107 return { answer, sources, sessionId };108}109110export function useChatMutation(onChunk?: (data: any) => void) {111 return useMutation({112 mutationFn: (request: ChatRequest) => streamChat(request, onChunk || (() => {})),113 });114}115