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%
5.9 KB · 173 lines typescript
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      server/services/claude/tokenManagement.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 { encode } from 'gpt-tokenizer';18import axios from 'axios';1920const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';21const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;22const ANTHROPIC_VERSION = '2023-06-01';23const DEFAULT_MODEL = 'claude-fable-5';2425// Estimate token count (rough approximation: 1 token ≈ 4 characters)26export function estimateTokens(text: string): number {27  return Math.ceil(text.length / 4);28}2930// Estimate tokens in messages array31export function estimateMessagesTokens(messages: any[]): number {32  let total = 0;33  for (const msg of messages) {34    if (typeof msg.content === 'string') {35      total += estimateTokens(msg.content);36    } else if (Array.isArray(msg.content)) {37      for (const block of msg.content) {38        if (block.type === 'text' && block.text) {39          total += estimateTokens(block.text);40        } else if (block.type === 'tool_result' && block.content) {41          if (typeof block.content === 'string') {42            total += estimateTokens(block.content);43          } else if (Array.isArray(block.content)) {44            for (const item of block.content) {45              if (item.type === 'text' && item.text) {46                total += estimateTokens(item.text);47              }48            }49          }50        }51      }52    }53  }54  return total;55}5657// Extract text content from messages, removing large tool results58export function extractTextContent(messages: any[]): string {59  let textContent = '';6061  for (const msg of messages) {62    if (msg.role === 'user') {63      if (typeof msg.content === 'string') {64        textContent += `User: ${msg.content}\n\n`;65      }66    } else if (msg.role === 'assistant') {67      if (typeof msg.content === 'string') {68        textContent += `Assistant: ${msg.content}\n\n`;69      } else if (Array.isArray(msg.content)) {70        // Extract only text blocks, skip tool_use blocks71        for (const block of msg.content) {72          if (block.type === 'text' && block.text) {73            textContent += `Assistant: ${block.text}\n\n`;74          }75        }76      }77    }78  }7980  // Truncate if still too long (keep last 10000 chars)81  if (textContent.length > 10000) {82    textContent = '...[earlier conversation truncated]...\n\n' + textContent.slice(-10000);83  }8485  return textContent;86}8788// Summarize old conversation history89export async function summarizeHistory(messages: any[], model: string = DEFAULT_MODEL): Promise<string> {90  try {91    const textContent = extractTextContent(messages);9293    const response = await axios.post(94      ANTHROPIC_API_URL,95      {96        model,97        max_tokens: 2000,98        temperature: 0.5,99        stream: false,100        system: 'You are a helpful assistant that summarizes conversation history. Create a concise but comprehensive summary of the key points, decisions, and context from the conversation history provided. Focus on financial queries, analysis topics, and important context.',101        messages: [102          {103            role: 'user',104            content: `Please summarize the following conversation history, focusing on key financial queries and analysis topics:\n\n${textContent}`105          }106        ],107      },108      {109        headers: {110          'Content-Type': 'application/json',111          'x-api-key': ANTHROPIC_API_KEY!,112          'anthropic-version': ANTHROPIC_VERSION,113        },114      }115    );116117    return response.data.content[0].text;118  } catch (error) {119    console.error('Error summarizing history:', error);120    const textContent = extractTextContent(messages);121    return `Previous conversation summary (${messages.length} messages):\n${textContent.slice(0, 500)}...`;122  }123}124125// Truncate tool results to avoid oversized messages126export function truncateToolResults(messages: any[], maxLength: number = 5000): any[] {127  return messages.map(msg => {128    if (!Array.isArray(msg.content)) return msg;129130    const truncatedContent = msg.content.map((block: any) => {131      if (block.type === 'tool_result' && block.content) {132        if (typeof block.content === 'string' && block.content.length > maxLength) {133          return {134            ...block,135            content: block.content.slice(0, maxLength) + '\n... [truncated for length]'136          };137        }138        if (Array.isArray(block.content)) {139          const textItems = block.content.filter((item: any) => item.type === 'text');140          if (textItems.length > 0) {141            let totalLength = 0;142            const truncatedItems = [];143            for (const item of textItems) {144              if (totalLength + item.text.length > maxLength) {145                truncatedItems.push({146                  type: 'text',147                  text: item.text.slice(0, maxLength - totalLength) + '\n... [truncated]'148                });149                break;150              }151              truncatedItems.push(item);152              totalLength += item.text.length;153            }154            return { ...block, content: truncatedItems };155          }156        }157      }158      return block;159    });160161    return { ...msg, content: truncatedContent };162  });163}164165// Calculate cost based on token usage166export function calculateCost(inputTokens: number, outputTokens: number): number {167  const inputCostPer1M = 3.00;168  const outputCostPer1M = 15.00;169  const inputCost = (inputTokens / 1_000_000) * inputCostPer1M;170  const outputCost = (outputTokens / 1_000_000) * outputCostPer1M;171  return inputCost + outputCost;172}173