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// Author: Simon-Pierre Boucher2// Contact: contact@spboucher.ai3// Project: chat.spboucher.ai45import type { ORModelRaw } from "./schemas";6import type { ModelDefinition } from "./types";78function toNumber(v: string | number | null | undefined): number | undefined {9 if (v === null || v === undefined) return undefined;10 const n = typeof v === "number" ? v : parseFloat(v);11 return Number.isFinite(n) ? n : undefined;12}1314/** Pricing values are USD per token (per request/image where applicable). */15export function derivePricing(raw: ORModelRaw): ModelDefinition["pricing"] {16 const p = raw.pricing ?? {};17 return {18 prompt: toNumber(p.prompt),19 completion: toNumber(p.completion),20 image: toNumber(p.image),21 request: toNumber(p.request),22 };23}2425/** Estimated cost in USD from token counts and per-token prices. */26export function estimateCost(27 pricing: ModelDefinition["pricing"] | undefined,28 promptTokens: number | undefined,29 completionTokens: number | undefined30): number | undefined {31 if (!pricing) return undefined;32 const inCost = pricing.prompt !== undefined && promptTokens !== undefined ? pricing.prompt * promptTokens : undefined;33 const outCost =34 pricing.completion !== undefined && completionTokens !== undefined35 ? pricing.completion * completionTokens36 : undefined;37 if (inCost === undefined && outCost === undefined) return undefined;38 return (inCost ?? 0) + (outCost ?? 0);39}4041/** Format a USD amount for display: $0.0042, $0.42, $4.20. */42export function formatUsd(v: number | undefined | null): string {43 if (v === undefined || v === null) return "—";44 if (v === 0) return "$0";45 if (v < 0.01) return `$${v.toFixed(4)}`;46 if (v < 1) return `$${v.toFixed(3)}`;47 return `$${v.toFixed(2)}`;48}4950/** Price per 1M tokens for the model rail chip. */51export function perMillion(perToken: number | undefined): string {52 if (perToken === undefined) return "—";53 const v = perToken * 1_000_000;54 if (v === 0) return "free";55 if (v < 1) return `$${v.toFixed(2)}/M`;56 return `$${v.toFixed(v < 10 ? 2 : 0)}/M`;57}58