SPB Git

spb/chat-spboucher Public

Private universal chat interface over the OpenRouter ecosystem — 400+ models, branching, streaming, usage tracking. Next.js 16 + SQLite, PWA, deployed on m4m64a at chat.spboucher.ai

TypeScript 78.8% CSS 15.1% JavaScript 4.9% Shell 1.2%
2.0 KB · 72 lines typescript
Raw Blame History
1// Author: Simon-Pierre Boucher2// Contact: contact@spboucher.ai3// Project: chat.spboucher.ai45/** Normalized model definition, decoupled from the OpenRouter wire format. */6export interface ModelDefinition {7  id: string; // exact OpenRouter model ID — opaque, sent back verbatim8  name: string;9  provider?: string;10  description?: string;11  contextLength?: number;12  pricing?: {13    prompt?: number; // USD per token14    completion?: number;15    image?: number;16    request?: number;17  };18  capabilities: {19    text: boolean;20    vision: boolean;21    reasoning: boolean;22    tools: boolean;23    structuredOutput: boolean;24  };25  metadata: {26    architecture?: string;27    tokenizer?: string;28    createdAt?: number;29    raw?: unknown;30  };31}3233/** Normalized application stream events — the browser never sees raw OpenRouter frames. */34export type ChatStreamEvent =35  | { type: "generation.start"; generationId: string; model: string }36  | { type: "content.delta"; text: string }37  | { type: "reasoning.delta"; text: string }38  | { type: "tool.start"; toolCallId: string; name: string }39  | { type: "tool.delta"; toolCallId: string; argumentsDelta: string }40  | {41      type: "usage";42      promptTokens?: number;43      completionTokens?: number;44      reasoningTokens?: number;45      cachedTokens?: number;46      totalTokens?: number;47      cost?: number;48    }49  | { type: "generation.end" }50  | { type: "generation.error"; message: string; retryable: boolean };5152export interface ChatMessageInput {53  role: "system" | "user" | "assistant";54  content: string;55}5657export interface GenerationRequest {58  model: string;59  messages: ChatMessageInput[];60  signal?: AbortSignal;61  temperature?: number;62  maxTokens?: number;63  /** Reserved for future per-generation routing config (provider prefs, fallbacks). */64  routing?: Record<string, unknown>;65}6667/** Narrow gateway abstraction so the frontend never becomes inseparable from OpenRouter. */68export interface ModelGateway {69  stream(request: GenerationRequest): AsyncIterable<ChatStreamEvent>;70  listModels(): Promise<ModelDefinition[]>;71}72