| 1 | /** |
| 2 | * How the AI Gateway page speaks of its requests. Pure, so it can be tested. |
| 3 | */ |
| 4 | |
| 5 | import type { GatewayRequest } from "@g1t/contracts"; |
| 6 | |
| 7 | /** Where the AI Gateway is documented. */ |
| 8 | export const GATEWAY_DOCS = "https://docs.g1t.sh/guides/ai-gateway/"; |
| 9 | |
| 10 | /** The base URL an Anthropic SDK or Claude Code is pointed at. */ |
| 11 | export const GATEWAY_BASE_URL = "https://models.g1t.sh/anthropic"; |
| 12 | |
| 13 | /** A count of tokens, short: `812`, `12.4K`, `3.1M`. */ |
| 14 | export function shortCount(n: number): string { |
| 15 | if (n < 1_000) return String(n); |
| 16 | if (n < 1_000_000) return `${trim(n / 1_000)}K`; |
| 17 | return `${trim(n / 1_000_000)}M`; |
| 18 | } |
| 19 | |
| 20 | function trim(value: number): string { |
| 21 | return value >= 100 ? String(Math.round(value)) : value.toFixed(1).replace(/\.0$/, ""); |
| 22 | } |
| 23 | |
| 24 | /** Every token a request used. */ |
| 25 | export function totalTokens(request: Pick<GatewayRequest, "input" | "output" | "cacheRead" | "cacheWrite">): number { |
| 26 | return request.input + request.output + request.cacheRead + request.cacheWrite; |
| 27 | } |
| 28 | |
| 29 | /** The tokens of each kind, in words, for a hint. */ |
| 30 | export function tokenKinds(request: Pick<GatewayRequest, "input" | "output" | "cacheRead" | "cacheWrite">): string { |
| 31 | const n = (v: number) => v.toLocaleString("en-US"); |
| 32 | return `${n(request.input)} input, ${n(request.output)} output, ${n(request.cacheRead)} cache read, ${n(request.cacheWrite)} cache write`; |
| 33 | } |
| 34 | |
| 35 | /** How a status reads, and how much it matters. */ |
| 36 | export function statusTone(status: number): { label: string; tone: "success" | "warn" | "danger" | "neutral" } { |
| 37 | if (status >= 200 && status < 300) return { label: String(status), tone: "success" }; |
| 38 | if (status === 402 || status === 429) return { label: String(status), tone: "warn" }; |
| 39 | if (status >= 400) return { label: String(status), tone: "danger" }; |
| 40 | return { label: String(status), tone: "neutral" }; |
| 41 | } |
| 42 | |
| 43 | /** A request's duration: `840 ms`, `4.2 s`, `2 min 5 s`. */ |
| 44 | export function duration(ms: number): string { |
| 45 | if (ms < 1_000) return `${Math.round(ms)} ms`; |
| 46 | if (ms < 60_000) return `${(ms / 1_000).toFixed(1).replace(/\.0$/, "")} s`; |
| 47 | const seconds = Math.round(ms / 1_000); |
| 48 | return `${Math.floor(seconds / 60)} min ${seconds % 60} s`; |
| 49 | } |