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%
9.6 KB · 233 lines typescript
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      shared/schema-sqlite.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 { sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";18import { createInsertSchema } from "drizzle-zod";19import { z } from "zod";2021// Re-export shared API types22export type { SearchResult, ClaudeMessage, ClaudeToolCall, ClaudeToolResult, ChatRequest, ChatResponse, ConversationMessage } from "./types";2324// Users table with token-based authentication25export const users = sqliteTable("users", {26  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),27  displayName: text("display_name").notNull(),28  token: text("token").notNull().unique(),29  createdAt: integer("created_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),30});3132// Crawled pages table - stores web content33export const crawledPages = sqliteTable("crawled_pages", {34  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),35  url: text("url").notNull().unique(),36  title: text("title").notNull(),37  content: text("content").notNull(),38  snippet: text("snippet"),39  favicon: text("favicon"),40  crawledAt: integer("crawled_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),41});4243// Embeddings table - stores vector embeddings for semantic search44// Note: SQLite doesn't have native vector support, we'll store as JSON text45export const embeddings = sqliteTable("embeddings", {46  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),47  pageId: text("page_id").notNull().unique().references(() => crawledPages.id, { onDelete: "cascade" }),48  embedding: text("embedding"), // JSON string of vector array49  tokenCount: integer("token_count").notNull(),50  createdAt: integer("created_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),51});5253// Chat messages table - stores conversation history54export const messages = sqliteTable("messages", {55  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),56  sessionId: text("session_id").notNull(),57  role: text("role").notNull(), // 'user' or 'assistant'58  content: text("content").notNull(),59  sources: text("sources"), // JSON array of URLs60  createdAt: integer("created_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),61});6263// Shared reports table - stores crystallized reports with unique links64export const sharedReports = sqliteTable("shared_reports", {65  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),66  shareId: text("share_id").notNull().unique(), // Unique short ID for the share link67  question: text("question").notNull(),68  answer: text("answer").notNull(),69  toolResults: text("tool_results"),70  sources: text("sources"),71  customPythonFigures: text("custom_python_figures"),72  createdAt: integer("created_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),73});7475// Conversation sessions table - stores complete conversation sessions with all data76export const conversationSessions = sqliteTable("conversation_sessions", {77  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),78  sessionId: text("session_id").notNull().unique(), // Session identifier79  userId: text("user_id").references(() => users.id, { onDelete: "cascade" }), // Owner of the session (null for anonymous)80  title: text("title").notNull(), // First question or custom title81  messages: text("messages").notNull(), // JSON array of { question, answer, toolResults, sources, searchQueries, pythonCode, monteCarloResults }82  inputTokens: integer("input_tokens").default(0), // Total input tokens used in this session83  outputTokens: integer("output_tokens").default(0), // Total output tokens used in this session84  totalCost: real("total_cost").default(0), // Total cost in USD (calculated as $3/$15 per million tokens)85  createdAt: integer("created_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),86  updatedAt: integer("updated_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),87});8889// Active users tracking - tracks who is currently generating content90export const activeUsers = sqliteTable("active_users", {91  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),92  sessionId: text("session_id").notNull().unique(), // Browser session ID (not conversation session)93  userId: text("user_id").references(() => users.id, { onDelete: "cascade" }), // User ID if logged in94  status: text("status").notNull().default("idle"), // idle, generating, error95  currentQuery: text("current_query"), // Current question being processed96  lastHeartbeat: integer("last_heartbeat", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),97  userAgent: text("user_agent"),98  ipAddress: text("ip_address"),99  createdAt: integer("created_at", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),100});101102// Analytics metrics - stores aggregated metrics over time103export const analyticsMetrics = sqliteTable("analytics_metrics", {104  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),105  timestamp: integer("timestamp", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),106107  // Request metrics108  totalRequests: integer("total_requests").default(0),109  successfulRequests: integer("successful_requests").default(0),110  failedRequests: integer("failed_requests").default(0),111112  // User metrics113  activeUsers: integer("active_users").default(0), // Count of users actively generating114  uniqueVisitors: integer("unique_visitors").default(0), // Unique sessions in this period115116  // Performance metrics117  averageResponseTime: real("average_response_time").default(0), // In milliseconds118  peakResponseTime: real("peak_response_time").default(0),119120  // Token metrics121  tokensGenerated: integer("tokens_generated").default(0),122  estimatedCost: real("estimated_cost").default(0),123124  // Tool usage125  toolCallsCount: integer("tool_calls_count").default(0),126  pythonExecutions: integer("python_executions").default(0),127  searchQueries: integer("search_queries").default(0),128129  // Error tracking130  errorCount: integer("error_count").default(0),131  errorRate: real("error_rate").default(0), // Percentage132133  // Period type (for aggregation)134  periodType: text("period_type").notNull().default("minute"), // minute, hour, day135});136137// Request logs - detailed logs for each request138export const requestLogs = sqliteTable("request_logs", {139  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),140  sessionId: text("session_id").notNull(),141  userId: text("user_id").references(() => users.id, { onDelete: "cascade" }),142143  query: text("query").notNull(),144  responseTime: real("response_time"), // In milliseconds145146  inputTokens: integer("input_tokens").default(0),147  outputTokens: integer("output_tokens").default(0),148  totalCost: real("total_cost").default(0),149150  toolsCalled: text("tools_called"), // JSON array of tool names151152  status: text("status").notNull(), // success, error, timeout153  errorMessage: text("error_message"),154155  timestamp: integer("timestamp", { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),156});157158// Zod schemas for validation159export const insertUserSchema = createInsertSchema(users).omit({160  id: true,161  createdAt: true,162});163164export const insertCrawledPageSchema = createInsertSchema(crawledPages).omit({165  id: true,166  crawledAt: true,167});168169export const insertEmbeddingSchema = createInsertSchema(embeddings).omit({170  id: true,171  createdAt: true,172});173174export const insertMessageSchema = createInsertSchema(messages).omit({175  id: true,176  createdAt: true,177});178179export const insertSharedReportSchema = createInsertSchema(sharedReports).omit({180  id: true,181  createdAt: true,182});183184export const insertConversationSessionSchema = createInsertSchema(conversationSessions).omit({185  id: true,186  createdAt: true,187  updatedAt: true,188});189190export const insertActiveUserSchema = createInsertSchema(activeUsers).omit({191  id: true,192  createdAt: true,193});194195export const insertAnalyticsMetricSchema = createInsertSchema(analyticsMetrics).omit({196  id: true,197  timestamp: true,198});199200export const insertRequestLogSchema = createInsertSchema(requestLogs).omit({201  id: true,202  timestamp: true,203});204205// TypeScript types206export type User = typeof users.$inferSelect;207export type InsertUser = z.infer<typeof insertUserSchema>;208209export type CrawledPage = typeof crawledPages.$inferSelect;210export type InsertCrawledPage = z.infer<typeof insertCrawledPageSchema>;211212export type Embedding = typeof embeddings.$inferSelect;213export type InsertEmbedding = z.infer<typeof insertEmbeddingSchema>;214215export type Message = typeof messages.$inferSelect;216export type InsertMessage = z.infer<typeof insertMessageSchema>;217218export type SharedReport = typeof sharedReports.$inferSelect;219export type InsertSharedReport = z.infer<typeof insertSharedReportSchema>;220221export type ConversationSession = typeof conversationSessions.$inferSelect;222export type InsertConversationSession = z.infer<typeof insertConversationSessionSchema>;223224export type ActiveUser = typeof activeUsers.$inferSelect;225export type InsertActiveUser = z.infer<typeof insertActiveUserSchema>;226227export type AnalyticsMetric = typeof analyticsMetrics.$inferSelect;228export type InsertAnalyticsMetric = z.infer<typeof insertAnalyticsMetricSchema>;229230export type RequestLog = typeof requestLogs.$inferSelect;231export type InsertRequestLog = z.infer<typeof insertRequestLogSchema>;232233