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%
1.9 KB · 69 lines typescript
Raw Blame History
1// Author: Simon-Pierre Boucher2// Contact: contact@spboucher.ai3// Project: chat.spboucher.ai45// Minimal runtime validation of OpenRouter wire shapes (no external dep).67export interface ORModelRaw {8  id: string;9  name?: string;10  description?: string;11  context_length?: number;12  created?: number;13  pricing?: Record<string, string | number | null>;14  architecture?: {15    modality?: string;16    input_modalities?: string[];17    output_modalities?: string[];18    tokenizer?: string;19    instruct_type?: string | null;20  };21  supported_parameters?: string[];22  top_provider?: { context_length?: number; max_completion_tokens?: number };23}2425export function parseModelsResponse(json: unknown): ORModelRaw[] {26  if (typeof json !== "object" || json === null) return [];27  const data = (json as { data?: unknown }).data;28  if (!Array.isArray(data)) return [];29  return data.filter(30    (m): m is ORModelRaw => typeof m === "object" && m !== null && typeof (m as ORModelRaw).id === "string"31  );32}3334/** One parsed SSE chunk from the OpenRouter chat stream. */35export interface ORStreamChunk {36  id?: string;37  choices?: Array<{38    delta?: {39      content?: string | null;40      reasoning?: string | null;41      tool_calls?: Array<{42        index?: number;43        id?: string;44        function?: { name?: string; arguments?: string };45      }>;46    };47    finish_reason?: string | null;48  }>;49  usage?: {50    prompt_tokens?: number;51    completion_tokens?: number;52    total_tokens?: number;53    cost?: number;54    completion_tokens_details?: { reasoning_tokens?: number };55    prompt_tokens_details?: { cached_tokens?: number };56  };57  error?: { message?: string; code?: number | string };58}5960export function parseStreamChunk(line: string): ORStreamChunk | null {61  try {62    const parsed = JSON.parse(line);63    if (typeof parsed !== "object" || parsed === null) return null;64    return parsed as ORStreamChunk;65  } catch {66    return null;67  }68}69