SPB Git

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%
2.9 KB · 115 lines typescript
Raw Blame History
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