feat: 品牌替换 + 启动优化 + AGENTS.md 模板定制

- 品牌替换:OpenCode/opencode → AirCoding/aircoding(16+ 文件)
- Logo ASCII art:修复 left/right 行数不匹配导致的启动崩溃
- 启动诊断:添加 OPENCODE_PRINT_TIMING 计时探针
- dev 模式默认 --pure 跳过外部插件加载
- AGENTS.md 模板:追加 AirCoding 多 Agent 专项段落
- architect prompt + plugin:强化 AGENTS.md 产出验证
This commit is contained in:
airlongdian
2026-06-14 09:31:29 +08:00
commit e2fd375a1c
5757 changed files with 1170016 additions and 0 deletions

1
packages/stats/AGENTS.md Normal file
View File

@@ -0,0 +1 @@
To start the stats site locally, run `bun dev:stats` from the repo root.

16
packages/stats/README.md Normal file
View File

@@ -0,0 +1,16 @@
# OpenCode Stats
Stats is a separate site from the console. Runtime, database, and domain services live in `core`; the SolidStart website lives in `app`; deployable Lambda entrypoints live in `function`.
## Packages
- `app`: SolidStart frontend/site.
- `core`: Effect services, app config, Drizzle schema/migrations, and stats domains.
- `function`: Lambda handlers that call into `core` services.
## Commands
- `bun run dev:stats` from the repo root starts the SolidStart app.
- `bun run --cwd packages/stats/app typecheck` typechecks the site.
- `bun run --cwd packages/stats/core typecheck` typechecks the Effect/database package.
- `bun run --cwd packages/stats/function typecheck` typechecks Lambda entrypoints.

17
packages/stats/app/.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
dist
.wrangler
.output
.vercel
.netlify
app.config.timestamp_*.js
# Environment
.env
.env*.local
# dependencies
/node_modules
# System Files
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,5 @@
export default {
server: {
preset: "cloudflare-module",
},
}

View File

@@ -0,0 +1,46 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-app",
"version": "1.17.4",
"private": true,
"type": "module",
"license": "MIT",
"scripts": {
"typecheck": "tsgo --noEmit",
"dev": "vite dev --host 0.0.0.0",
"build": "vite build",
"start": "vite start"
},
"dependencies": {
"@ibm/plex": "6.4.1",
"@opencode-ai/stats-core": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@solidjs/meta": "catalog:",
"@solidjs/router": "catalog:",
"@solidjs/start": "catalog:",
"d3-geo": "3.1.1",
"d3-scale": "4.0.2",
"effect": "catalog:",
"i18n-iso-countries": "7.14.0",
"nitro": "3.0.1-alpha.1",
"solid-js": "catalog:",
"sst": "catalog:",
"topojson-client": "3.1.0",
"vite": "catalog:",
"world-atlas": "2.0.2"
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:",
"@types/bun": "catalog:",
"@types/d3-geo": "3.1.0",
"@types/d3-scale": "4.0.9",
"@types/geojson": "7946.0.16",
"@types/topojson-client": "3.1.5",
"@types/topojson-specification": "1.0.5",
"@typescript/native-preview": "catalog:",
"typescript": "catalog:"
},
"engines": {
"node": ">=22"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -0,0 +1,123 @@
:root {
color-scheme: light dark;
--stats-bg: #f8f5ee;
--stats-ink: #16110d;
--stats-muted: #6d6257;
--stats-line: #ded5c9;
--stats-panel: #fffaf1;
--stats-accent: #2357ff;
}
@media (prefers-color-scheme: dark) {
:root {
--stats-bg: #11100e;
--stats-ink: #f7efe4;
--stats-muted: #b8aa99;
--stats-line: #322d27;
--stats-panel: #1a1714;
--stats-accent: #86a2ff;
}
}
html {
line-height: 1;
background: var(--stats-bg);
}
body {
margin: 0;
min-width: 320px;
background:
radial-gradient(circle at top left, color-mix(in srgb, var(--stats-accent) 16%, transparent), transparent 32rem),
var(--stats-bg);
color: var(--stats-ink);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
-webkit-font-smoothing: antialiased;
}
a {
color: inherit;
}
.shell {
box-sizing: border-box;
min-height: 100vh;
padding: 2rem clamp(1rem, 4vw, 4rem);
}
.panel {
display: grid;
gap: clamp(2rem, 8vw, 5rem);
box-sizing: border-box;
width: min(100%, 72rem);
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 3rem);
border: 1px solid var(--stats-line);
border-radius: 1.5rem;
background: color-mix(in srgb, var(--stats-panel) 88%, transparent);
}
.eyebrow {
margin: 0 0 1rem;
color: var(--stats-muted);
font-size: 0.75rem;
letter-spacing: 0.14em;
text-transform: uppercase;
}
h1 {
max-width: 11ch;
margin: 0;
font-size: clamp(3rem, 14vw, 9rem);
line-height: 0.85;
letter-spacing: -0.08em;
}
.summary {
max-width: 42rem;
margin: 1.5rem 0 0;
color: var(--stats-muted);
font-size: clamp(1rem, 2vw, 1.25rem);
line-height: 1.6;
}
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1px;
overflow: hidden;
border: 1px solid var(--stats-line);
border-radius: 1rem;
background: var(--stats-line);
}
.metric {
padding: 1rem;
background: var(--stats-panel);
}
.metric b {
display: block;
margin-bottom: 0.5rem;
font-size: clamp(1.5rem, 4vw, 3rem);
letter-spacing: -0.05em;
}
.metric span {
color: var(--stats-muted);
font-size: 0.8125rem;
}
.link {
display: inline-flex;
width: fit-content;
margin-top: 1.5rem;
color: var(--stats-accent);
text-decoration: none;
}
@media (max-width: 720px) {
.grid {
grid-template-columns: 1fr;
}
}

View File

@@ -0,0 +1,31 @@
import { MetaProvider, Meta, Title } from "@solidjs/meta"
import { Router } from "@solidjs/router"
import { FileRoutes } from "@solidjs/start/router"
import { Suspense } from "solid-js"
import "./app.css"
function AppMeta() {
return (
<>
<Title>OpenCode Data</Title>
<Meta name="description" content="OpenCode usage data, market share, token cost, and session cost." />
</>
)
}
export default function App() {
return (
<Router
base={import.meta.env.BASE_URL.replace(/\/$/, "")}
explicitLinks={true}
root={(props) => (
<MetaProvider>
<AppMeta />
<Suspense>{props.children}</Suspense>
</MetaProvider>
)}
>
<FileRoutes />
</Router>
)
}

View File

@@ -0,0 +1,18 @@
<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 30H6V18H18V30Z" fill="#4B4646"/>
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#B7B1B1"/>
<path d="M48 30H36V18H48V30Z" fill="#4B4646"/>
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#B7B1B1"/>
<path d="M84 24V30H66V24H84Z" fill="#4B4646"/>
<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#B7B1B1"/>
<path d="M108 36H96V18H108V36Z" fill="#4B4646"/>
<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#B7B1B1"/>
<path d="M144 30H126V18H144V30Z" fill="#4B4646"/>
<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#F1ECEC"/>
<path d="M168 30H156V18H168V30Z" fill="#4B4646"/>
<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#F1ECEC"/>
<path d="M198 30H186V18H198V30Z" fill="#4B4646"/>
<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#F1ECEC"/>
<path d="M234 24V30H216V24H234Z" fill="#4B4646"/>
<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#F1ECEC"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,18 @@
<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 30H6V18H18V30Z" fill="#CFCECD"/>
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#656363"/>
<path d="M48 30H36V18H48V30Z" fill="#CFCECD"/>
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#656363"/>
<path d="M84 24V30H66V24H84Z" fill="#CFCECD"/>
<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#656363"/>
<path d="M108 36H96V18H108V36Z" fill="#CFCECD"/>
<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#656363"/>
<path d="M144 30H126V18H144V30Z" fill="#CFCECD"/>
<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#211E1E"/>
<path d="M168 30H156V18H168V30Z" fill="#CFCECD"/>
<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#211E1E"/>
<path d="M198 30H186V18H198V30Z" fill="#CFCECD"/>
<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#211E1E"/>
<path d="M234 24V30H216V24H234Z" fill="#CFCECD"/>
<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#211E1E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,7 @@
// @refresh reload
import { mount, StartClient } from "@solidjs/start/client"
const root = document.getElementById("app")
if (!root) throw new Error("Root element #app not found")
mount(() => <StartClient />, root)

View File

@@ -0,0 +1,37 @@
// @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server"
const statsThemePreloadScript = `;(function () {
var preference = "system"
try {
var stored = localStorage.getItem("opencode:stats-theme")
if (stored === "dark" || stored === "light" || stored === "system") preference = stored
} catch (_) {}
document.documentElement.dataset.statsTheme = preference
if (preference === "system") document.documentElement.style.removeProperty("color-scheme")
else document.documentElement.style.setProperty("color-scheme", preference)
})()`
export default createHandler(
() => (
<StartServer
document={({ assets, children, scripts }) => (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script id="stats-theme-preload-script">{statsThemePreloadScript}</script>
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
),
{
mode: "async",
},
)

1
packages/stats/app/src/global.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="@solidjs/start/env" />

10
packages/stats/app/src/resource.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
import "sst/resource"
declare module "sst/resource" {
export interface Resource {
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
}
}

View File

@@ -0,0 +1,836 @@
import "../index.css"
import { Link, Meta, Title } from "@solidjs/meta"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { geoEquirectangular, geoPath } from "d3-geo"
import { scaleSqrt } from "d3-scale"
import countryCodesSource from "i18n-iso-countries/codes.json?raw"
import { feature, mesh } from "topojson-client"
import countriesTopologySource from "world-atlas/countries-110m.json?raw"
import {
getStatsModelData,
type CountryEntry,
type ModelPeerEntry,
type ModelUsagePoint,
type StatsModelData,
type UsageRange,
} from "@opencode-ai/stats-core/domain/home"
import { runtime } from "@opencode-ai/stats-core/runtime"
import { createAsync, query, useParams } from "@solidjs/router"
import { createMemo, createSignal, For, onMount, Show, type JSX } from "solid-js"
import { getRequestEvent } from "solid-js/web"
import type { FeatureCollection, GeometryObject, GeoJsonProperties } from "geojson"
import type { GeometryCollection, Topology } from "topojson-specification"
import { findModelCatalogEntry, formatCatalogLabName, getModelCatalog, type ModelCatalogEntry } from "../model-catalog"
import {
applyThemePreference,
Footer,
getGitHubStars,
Header,
isThemePreference,
themeStorageKey,
type HeaderLink,
type ThemePreference,
} from "../stats-shell"
const statsCanonicalBaseUrl = "https://opencode.ai/data/"
const statsUnfurlPath = "banner.png"
const statsUnfurlAlt = "OpenCode Data wordmark on a dark patterned background"
const statsUnfurlUrl = new URL(statsUnfurlPath, statsCanonicalBaseUrl).toString()
const modelHeaderLinks: readonly HeaderLink[] = [
{ href: "#overview", label: "Overview" },
{ href: "#usage", label: "Usage" },
{ href: "#efficiency", label: "Efficiency" },
{ href: "#geo-breakdown", label: "Geo Breakdown" },
{ href: "#peers", label: "Peers" },
]
const modelFooterLinks: readonly HeaderLink[] = [
{ href: import.meta.env.BASE_URL, label: "Data Home" },
{ href: `${import.meta.env.BASE_URL}#top-models`, label: "Top Models" },
{ href: `${import.meta.env.BASE_URL}#leaderboard`, label: "Leaderboard" },
{ href: `${import.meta.env.BASE_URL}#session-cost`, label: "Session Cost" },
{ href: `${import.meta.env.BASE_URL}#token-cost`, label: "Token Cost" },
{ href: `${import.meta.env.BASE_URL}#market-share`, label: "Market Share" },
{ href: `${import.meta.env.BASE_URL}#geo-breakdown`, label: "Geo Breakdown" },
]
const geoMapWidth = 960
const geoMapHeight = 430
const countryDisplayNames = new Intl.DisplayNames(["en"], { type: "region" })
type IsoCountryCode = readonly [string, string, string]
type WorldCountryProperties = GeoJsonProperties & { name?: string }
type WorldTopology = Topology<{ countries: GeometryCollection<WorldCountryProperties> }>
const countryNumericIds = new Map(
(JSON.parse(countryCodesSource) as IsoCountryCode[]).map((country) => [country[0], country[2]] as const),
)
const worldTopology = JSON.parse(countriesTopologySource) as WorldTopology
const worldCountryGeometries: GeometryCollection<WorldCountryProperties> = {
...worldTopology.objects.countries,
geometries: worldTopology.objects.countries.geometries.filter((country) => String(country.id ?? "") !== "010"),
}
const worldCountries = feature<WorldCountryProperties>(worldTopology, worldCountryGeometries) as FeatureCollection<
GeometryObject,
WorldCountryProperties
>
const worldProjection = geoEquirectangular().fitExtent(
[
[10, 12],
[geoMapWidth - 10, geoMapHeight - 12],
],
worldCountries,
)
const worldPath = geoPath(worldProjection)
const worldCountryPaths = worldCountries.features.map((country) => ({
id: String(country.id ?? "").padStart(3, "0"),
path: worldPath(country) ?? "",
}))
const worldBorderPath = worldPath(mesh(worldTopology, worldCountryGeometries, (a, b) => a !== b)) ?? ""
const getModelData = query(async (lab: string, model: string) => {
"use server"
return runtime.runPromise(getStatsModelData(model, lab))
}, "getStatsModelData")
export default function StatsModel() {
const event = getRequestEvent()
event?.response.headers.set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=86400")
const params = useParams()
const labParam = createMemo(() => params.lab ?? "")
const modelParam = createMemo(() => params.model ?? "")
const catalog = createAsync(() => getModelCatalog())
const catalogEntry = createMemo(() => {
const data = catalog()
if (!data) return undefined
return findModelCatalogEntry(data, modelParam(), labParam()) ?? null
})
const stats = createAsync(() => {
const entry = catalogEntry()
if (catalog() === undefined || entry === undefined) return Promise.resolve(undefined)
if (!entry && (!labParam() || !modelParam())) return Promise.resolve(null)
return getModelData(labParam(), entry?.slug ?? modelParam())
})
const githubStars = createAsync(() => getGitHubStars())
const [themePreference, setThemePreference] = createSignal<ThemePreference>("system")
const modelName = createMemo(() => catalogEntry()?.name ?? stats()?.model ?? modelParam() ?? "Model")
const labName = createMemo(() => formatCatalogLabName(catalogEntry()?.lab ?? stats()?.provider ?? labParam()))
const modelTitle = createMemo(() => `${modelName()} Data`)
const modelDescription = createMemo(() =>
stats()
? `${modelName()} usage, rank, token mix, cost, geo breakdown, and peer data across OpenCode.`
: `${modelName()} model facts, limits, and OpenCode usage availability.`,
)
const modelUrl = createMemo(() =>
new URL(
catalogEntry()?.id ?? [labParam(), stats()?.slug ?? modelParam()].filter((part) => part.length > 0).join("/"),
statsCanonicalBaseUrl,
).toString(),
)
const updateThemePreference = (preference: ThemePreference) => {
applyThemePreference(preference)
setThemePreference(preference)
if (typeof window === "undefined") return
window.localStorage.setItem(themeStorageKey, preference)
}
onMount(() => {
if (typeof window === "undefined") return
const preference = window.localStorage.getItem(themeStorageKey)
const nextPreference = isThemePreference(preference) ? preference : "system"
applyThemePreference(nextPreference)
setThemePreference(nextPreference)
})
return (
<main data-page="stats" data-theme={themePreference()}>
<Title>{modelTitle()}</Title>
<Meta name="description" content={modelDescription()} />
<Link rel="canonical" href={modelUrl()} />
<Meta property="og:type" content="website" />
<Meta property="og:site_name" content="OpenCode" />
<Meta property="og:title" content={modelTitle()} />
<Meta property="og:description" content={modelDescription()} />
<Meta property="og:url" content={modelUrl()} />
<Meta property="og:image" content={statsUnfurlUrl} />
<Meta property="og:image:type" content="image/png" />
<Meta property="og:image:width" content="1200" />
<Meta property="og:image:height" content="630" />
<Meta property="og:image:alt" content={statsUnfurlAlt} />
<Meta name="twitter:card" content="summary_large_image" />
<Meta name="twitter:title" content={modelTitle()} />
<Meta name="twitter:description" content={modelDescription()} />
<Meta name="twitter:image" content={statsUnfurlUrl} />
<Meta name="twitter:image:alt" content={statsUnfurlAlt} />
<Header githubStars={githubStars() ?? "150K"} links={modelHeaderLinks} brandHref={import.meta.env.BASE_URL} />
<div data-component="container">
<div data-component="content">
<Show when={catalogEntry() || stats() !== undefined} fallback={<ModelLoading />}>
<Show when={catalogEntry() || stats()} fallback={<ModelNotFound lab={labParam()} model={modelParam()} />}>
<>
<ModelHero data={stats() ?? null} catalog={catalogEntry() ?? null} labName={labName()} />
<ModelOverview data={stats() ?? null} />
<ModelUsageSection data={stats()?.usage ?? []} />
<ModelEfficiencySection data={stats() ?? null} />
<ModelGeoBreakdownSection data={stats()?.country ?? emptyCountryRecord()} />
<ModelPeersSection data={stats() ?? null} />
</>
</Show>
</Show>
</div>
<Footer
themePreference={themePreference()}
onThemePreferenceChange={updateThemePreference}
links={modelFooterLinks}
/>
</div>
</main>
)
}
function ModelLoading() {
return (
<>
<section id="overview" data-section="model-hero">
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<a data-slot="model-back-link" href={import.meta.env.BASE_URL}>
Data
</a>
<h1>Model Data</h1>
<p>Reading model aggregates from model_stat.</p>
</div>
</div>
</section>
<section data-section="model-panel">
<ModelEmptyState title="Loading model data" description="Reading the model profile." />
</section>
</>
)
}
function ModelNotFound(props: { lab: string; model: string }) {
return (
<>
<section id="overview" data-section="model-hero">
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<a data-slot="model-back-link" href={import.meta.env.BASE_URL}>
Data
</a>
<h1>{props.model || "Model"}</h1>
<p>No model facts or model_stat rows matched {props.lab ? `${props.lab}/${props.model}` : props.model}.</p>
</div>
</div>
</section>
<section data-section="model-panel">
<ModelEmptyState title="No model data" description="Try opening a model from the leaderboard." />
</section>
</>
)
}
function ModelHero(props: { data: StatsModelData | null; catalog: ModelCatalogEntry | null; labName: string }) {
const labId = () => props.catalog?.lab ?? props.data?.provider ?? props.labName
const modelId = () => props.catalog?.id ?? props.data?.model ?? "Model"
const weights = () => props.catalog?.weights[0]
return (
<section id="overview" data-section="model-hero">
<a data-slot="model-back-link" href={import.meta.env.BASE_URL}>
Data
</a>
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<div data-slot="model-hero-tags">
<a data-slot="hero-meta" href={`${import.meta.env.BASE_URL}${providerSlug(labId())}`}>
<ProviderIcon aria-hidden="true" id={getProviderIconId(labId())} />
<span>{props.labName}</span>
</a>
<span data-slot="model-id-tag">{modelId()}</span>
</div>
<h1>{props.catalog?.name ?? props.data?.model ?? "Model"}</h1>
<Show
when={props.data}
fallback={
<p>Model facts from the shared model index. OpenCode usage appears once this model has activity.</p>
}
>
{(data) => (
<p>
Ranked #{data().rank} across recent OpenCode token usage with {formatPercent(data().tokenShare)} of
observed volume.
</p>
)}
</Show>
<Show when={props.catalog?.openWeights && weights()}>
{(weight) => (
<a data-slot="model-weight-link" href={weight().url} target="_blank" rel="noopener noreferrer">
Model weights: {weight().label}
</a>
)}
</Show>
</div>
<Show when={props.data} fallback={<ModelCatalogCallout catalog={props.catalog} />}>
{(data) => (
<div data-component="model-rank-panel">
<span>Current Rank</span>
<strong>#{data().rank}</strong>
<p>{formatRankMoveLabel(data().previousRank, data().rank)}</p>
</div>
)}
</Show>
</div>
<div data-slot="model-hero-pattern" aria-hidden="true" />
<Show when={props.catalog}>{(catalog) => <ModelCatalogPanel data={catalog()} />}</Show>
</section>
)
}
function ModelCatalogCallout(props: { catalog: ModelCatalogEntry | null }) {
return (
<div data-component="model-rank-panel">
<span>Model Profile</span>
<strong>{props.catalog?.releaseDate ? formatCatalogDate(props.catalog.releaseDate) : "Listed"}</strong>
<p>No OpenCode usage in the current data window.</p>
</div>
)
}
function ModelCatalogPanel(props: { data: ModelCatalogEntry }) {
return (
<aside data-component="model-catalog" aria-label="Model facts">
<div data-slot="model-catalog-grid">
<CatalogDatum label="Context" value={formatCatalogLimit(props.data.limit?.context)} />
<CatalogDatum label="Output" value={formatCatalogLimit(props.data.limit?.output)} />
<CatalogDatum label="Knowledge" value={formatCatalogDate(props.data.knowledge)} />
<CatalogDatum label="Release" value={formatCatalogDate(props.data.releaseDate)} />
<CatalogDatum label="Inputs" value={formatCatalogModalities(props.data.modalities.input)} />
</div>
</aside>
)
}
function CatalogDatum(props: { label: string; value: string }) {
return (
<article data-component="model-catalog-datum">
<span>{props.label}</span>
<strong>{props.value}</strong>
</article>
)
}
function ModelOverview(props: { data: StatsModelData | null }) {
return (
<section data-section="model-panel">
<SectionTitle title="Overview" description="Recent tokens, sessions, and market position." />
<Show
when={props.data}
fallback={<ModelEmptyState title="No usage summary" description="This model has no OpenCode usage rows yet." />}
>
{(data) => (
<div data-component="model-metric-grid">
<MetricCard label="Tokens" value={formatTokens(data().totals.tokens)} detail="last two months" />
<MetricCard label="Sessions" value={formatInteger(data().totals.sessions)} detail="completed sessions" />
<MetricCard
label="Token Share"
value={formatPercent(data().tokenShare)}
detail={`${data().totalModels} models`}
/>
<MetricCard
label="Momentum"
value={formatChange(data().tokenChange)}
detail="vs previous window"
state={data().tokenChange < 0 ? "negative" : "positive"}
/>
</div>
)}
</Show>
</section>
)
}
function ModelUsageSection(props: { data: ModelUsagePoint[] }) {
const [activeIndex, setActiveIndex] = createSignal<number>()
const max = createMemo(() => Math.max(0, ...props.data.map((item) => item.tokens)) || 1)
const activePoint = createMemo(() => {
const index = activeIndex()
if (index === undefined) return undefined
return props.data[index]
})
return (
<section id="usage" data-section="model-panel">
<SectionTitle title="Usage" description="Daily token volume over the recent two-month window." />
<Show
when={props.data.some((item) => item.tokens > 0)}
fallback={<ModelEmptyState title="No usage" description="No usage landed in the current window." />}
>
<div
data-component="model-usage-chart"
data-dense-labels={isModelUsageDense(props.data.length) ? "true" : undefined}
role="img"
aria-label="Daily token usage chart"
style={{ "--model-usage-count": props.data.length } as JSX.CSSProperties}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(undefined)
}}
>
<div data-slot="model-usage-axis" aria-hidden="true">
<For each={props.data}>
{(point, index) => (
<div
data-active={activeIndex() === index() ? "true" : undefined}
data-label-hidden={isModelUsageLabelHidden(index(), props.data.length) ? "true" : undefined}
>
<span data-slot="model-usage-label">
<span data-slot="model-usage-total">{formatTokens(point.tokens)}</span>
<span data-slot="model-usage-date">{point.date}</span>
</span>
</div>
)}
</For>
</div>
<div data-slot="model-usage-bars">
<For each={props.data}>
{(point, index) => (
<div
data-slot="model-usage-column"
role="button"
tabIndex={0}
aria-label={`${point.date} ${formatTokens(point.tokens)} tokens`}
data-active={activeIndex() === index() ? "true" : undefined}
data-muted={activeIndex() !== undefined && activeIndex() !== index() ? "true" : undefined}
onPointerDown={(event) => {
if (event.pointerType !== "touch") return
setActiveIndex(index())
}}
onPointerEnter={() => setActiveIndex(index())}
onPointerMove={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(index())
}}
onClick={() => setActiveIndex(index())}
onFocus={() => setActiveIndex(index())}
onBlur={() => setActiveIndex(undefined)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
setActiveIndex(index())
}}
>
<div
data-slot="model-usage-bar"
style={{ "--model-usage-fill": `${modelUsageHeight(point.tokens, max())}%` } as JSX.CSSProperties}
/>
<Show when={activeIndex() === index() && activePoint()}>
{(active) => (
<div
data-component="chart-tooltip"
data-placement={index() > props.data.length * 0.62 ? "left" : "right"}
>
<strong>{active().date}</strong>
<span>{formatTokens(active().tokens)} tokens</span>
<div data-slot="tooltip-divider" />
<p>
<span data-slot="tooltip-label">
<i /> Daily tokens
</span>
<b>{formatTokens(active().tokens)}</b>
</p>
</div>
)}
</Show>
</div>
)}
</For>
</div>
</div>
</Show>
</section>
)
}
function ModelEfficiencySection(props: { data: StatsModelData | null }) {
return (
<section id="efficiency" data-section="model-panel">
<SectionTitle title="Efficiency" description="Cost, cache behavior, and average session shape." />
<Show
when={props.data}
fallback={
<ModelEmptyState title="No efficiency data" description="Efficiency data appears after usage lands." />
}
>
{(data) => (
<div data-component="model-metric-grid" data-variant="dense">
<MetricCard label="Cost" value={formatMoney(data().totals.cost)} detail="total spend" />
<MetricCard label="Cost / 1M" value={formatMoney(data().totals.costPerMillion)} detail="all tokens" />
<MetricCard
label="Cost / Session"
value={formatSessionCost(data().totals.costPerSession)}
detail="average"
/>
<MetricCard
label="Tokens / Session"
value={formatTokens(data().totals.tokensPerSession)}
detail="average"
/>
<MetricCard label="Cache Ratio" value={formatPercent(data().totals.cacheRatio)} detail="input tokens" />
</div>
)}
</Show>
</section>
)
}
function ModelGeoBreakdownSection(props: { data: Record<UsageRange, CountryEntry[]> }) {
const [activeCountry, setActiveCountry] = createSignal<string>()
const data = createMemo(() => props.data["2M"])
const countryById = createMemo(
() =>
new Map(
data().flatMap((country) => {
const id = countryNumericId(country.country)
return id ? [[id, country] as const] : []
}),
),
)
const maxTokens = createMemo(() => Math.max(0, ...data().map((country) => country.tokens)) || 1)
const topCountries = createMemo(() => data().slice(0, 15))
const active = createMemo(() => data().find((country) => country.country === activeCountry()) ?? data()[0])
return (
<section
id="geo-breakdown"
data-section="geo-breakdown"
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
setActiveCountry(undefined)
}}
>
<SectionTitle title="Geo Breakdown" description="Model tokens used by country." />
<Show
when={data().length > 0}
fallback={<ModelEmptyState title="No geo data" description="No geo_stat rows matched this model." />}
>
<div data-component="geo-breakdown">
<div data-slot="geo-map-panel">
<GeoWorldMap
countryById={countryById()}
activeCountry={activeCountry()}
maxTokens={maxTokens()}
onActiveCountryChange={setActiveCountry}
/>
<Show when={active()}>
{(country) => (
<div data-slot="geo-active-country">
<span>#{String(country().rank).padStart(2, "0")}</span>
<strong>{formatCountryName(country().country)}</strong>
<p>
<b>{formatGeoTokens(country().tokens)}</b>
<em>{formatGeoShare(country().share)}</em>
</p>
</div>
)}
</Show>
</div>
<GeoCountryList
data={topCountries()}
activeCountry={activeCountry()}
maxTokens={maxTokens()}
onActiveCountryChange={setActiveCountry}
/>
</div>
</Show>
</section>
)
}
function GeoWorldMap(props: {
countryById: Map<string, CountryEntry>
activeCountry: string | undefined
maxTokens: number
onActiveCountryChange: (country: string | undefined) => void
}) {
const opacityScale = createMemo(() => scaleSqrt().domain([0, props.maxTokens]).range([0.26, 0.96]).clamp(true))
const countryOpacity = (country: CountryEntry | undefined) => {
if (!country) return 0
const opacity = opacityScale()(country.tokens)
if (!props.activeCountry || props.activeCountry === country.country) return opacity
return Math.max(0.18, opacity * 0.36)
}
return (
<svg
data-component="geo-world-map"
viewBox={`0 0 ${geoMapWidth} ${geoMapHeight}`}
role="img"
aria-label="World map of model token usage by country"
>
<title>Geo Breakdown map</title>
<g data-slot="geo-countries">
<For each={worldCountryPaths}>
{(country) => {
const entry = () => props.countryById.get(country.id)
return (
<path
d={country.path}
data-has-data={entry() ? "true" : undefined}
data-active={entry()?.country === props.activeCountry ? "true" : undefined}
style={{ "--geo-country-opacity": String(countryOpacity(entry())) } as JSX.CSSProperties}
aria-hidden="true"
onPointerEnter={() => {
const item = entry()
if (!item) return
props.onActiveCountryChange(item.country)
}}
onClick={() => {
const item = entry()
if (!item) return
props.onActiveCountryChange(item.country)
}}
/>
)
}}
</For>
</g>
<path data-slot="geo-borders" d={worldBorderPath} aria-hidden="true" />
</svg>
)
}
function GeoCountryList(props: {
data: CountryEntry[]
activeCountry: string | undefined
maxTokens: number
onActiveCountryChange: (country: string | undefined) => void
}) {
const opacityScale = createMemo(() => scaleSqrt().domain([0, props.maxTokens]).range([0.26, 0.96]).clamp(true))
return (
<ol data-component="geo-country-list">
<For each={props.data}>
{(country) => (
<li>
<button
type="button"
data-active={props.activeCountry === country.country ? "true" : undefined}
style={{ "--geo-row-opacity": String(opacityScale()(country.tokens)) } as JSX.CSSProperties}
aria-label={`${formatCountryName(country.country)} ${formatGeoTokens(country.tokens)} ${formatGeoShare(
country.share,
)}`}
onClick={() => props.onActiveCountryChange(country.country)}
onPointerEnter={() => props.onActiveCountryChange(country.country)}
onFocus={() => props.onActiveCountryChange(country.country)}
>
<span>{String(country.rank).padStart(2, "0")}</span>
<i />
<strong>{formatCountryName(country.country)}</strong>
<em>{formatGeoTokens(country.tokens)}</em>
<b>{formatGeoShare(country.share)}</b>
</button>
</li>
)}
</For>
</ol>
)
}
function ModelPeersSection(props: { data: StatsModelData | null }) {
return (
<section id="peers" data-section="model-panel">
<SectionTitle title="Peers" description="Nearby models by recent token volume." />
<Show
when={props.data?.peers.length}
fallback={<ModelEmptyState title="No peers" description="Peer rankings appear after usage lands." />}
>
<ol data-component="model-peer-list">
<For each={props.data?.peers ?? []}>
{(peer) => <PeerRow peer={peer} active={peer.model === props.data?.model} />}
</For>
</ol>
</Show>
</section>
)
}
function MetricCard(props: { label: string; value: string; detail: string; state?: "positive" | "negative" }) {
return (
<article data-component="model-metric" data-state={props.state}>
<span>{props.label}</span>
<strong>{props.value}</strong>
<p>{props.detail}</p>
</article>
)
}
function PeerRow(props: { peer: ModelPeerEntry; active: boolean }) {
return (
<li>
<a
href={`${import.meta.env.BASE_URL}${providerSlug(props.peer.provider)}/${props.peer.slug}`}
data-active={props.active ? "true" : undefined}
>
<span>{String(props.peer.rank).padStart(2, "0")}</span>
<ProviderIcon aria-hidden="true" id={getProviderIconId(props.peer.author)} />
<strong>{props.peer.model}</strong>
<em>{props.peer.author}</em>
<b>{formatTokens(props.peer.tokens)}</b>
</a>
</li>
)
}
function SectionTitle(props: { title: string; description: string }) {
return (
<p data-slot="section-title">
<strong>{props.title}.</strong> <span>{props.description}</span>
</p>
)
}
function ModelEmptyState(props: { title: string; description: string; compact?: boolean }) {
return (
<div data-component="empty-state" data-compact={props.compact ? "true" : undefined}>
<strong>{props.title}</strong>
<p>{props.description}</p>
</div>
)
}
function getProviderIconId(author: string) {
if (author === "MiniMax") return "minimax"
if (author === "Moonshot") return "moonshotai"
if (author === "Zhipu") return "zhipuai"
return author.toLowerCase().replace(/[^a-z0-9]+/g, "")
}
function emptyCountryRecord(): Record<UsageRange, CountryEntry[]> {
return {
"1D": [],
"1W": [],
"2W": [],
"1M": [],
"2M": [],
"3M": [],
YTD: [],
ALL: [],
}
}
function countryNumericId(country: string) {
return countryNumericIds.get(country.toUpperCase())?.padStart(3, "0")
}
function formatCountryName(country: string) {
const code = country.toUpperCase()
if (code === "ZZ") return "Unknown"
if (!countryNumericId(code)) return code
return countryDisplayNames.of(code) ?? code
}
function formatGeoTokens(value: number) {
return formatTokens(value * 1_000_000_000_000)
}
function formatGeoShare(value: number) {
return `${value.toFixed(value > 0 && value < 1 ? 1 : 0)}%`
}
function modelUsageHeight(tokens: number, max: number) {
if (tokens <= 0) return 0
return Math.max(2, Math.min(100, (tokens / max) * 100))
}
function isModelUsageDense(count: number) {
return count > 20
}
function isModelUsageLabelHidden(index: number, count: number) {
if (count <= 16) return false
const interval = Math.ceil(count / 8)
return index !== count - 1 && index % interval !== 0
}
function formatRankMove(previousRank: number, rank: number) {
const change = previousRank - rank
if (change > 0) return `+${change}`
if (change < 0) return `${change}`
return "Even"
}
function formatRankMoveLabel(previousRank: number | null, rank: number) {
return previousRank === null ? "New in window" : `${formatRankMove(previousRank, rank)} vs previous window`
}
function formatTokens(value: number) {
if (value >= 1_000_000_000_000)
return `${trimNumber(value / 1_000_000_000_000, value >= 10_000_000_000_000 ? 0 : 1)}T`
if (value >= 1_000_000_000) return `${trimNumber(value / 1_000_000_000, value >= 10_000_000_000 ? 0 : 1)}B`
if (value >= 1_000_000) return `${trimNumber(value / 1_000_000, value >= 10_000_000 ? 0 : 1)}M`
if (value >= 1_000) return `${trimNumber(value / 1_000, value >= 10_000 ? 0 : 1)}K`
return String(Math.round(value))
}
function formatInteger(value: number) {
return new Intl.NumberFormat("en").format(value)
}
function formatPercent(value: number) {
return `${value.toFixed(value > 0 && value < 10 ? 1 : 0)}%`
}
function formatMoney(value: number) {
if (value >= 1_000_000) return `$${trimNumber(value / 1_000_000, value >= 10_000_000 ? 0 : 1)}M`
if (value >= 1_000) return `$${trimNumber(value / 1_000, value >= 10_000 ? 0 : 1)}K`
return `$${value.toFixed(value >= 10 ? 0 : 2)}`
}
function formatSessionCost(value: number) {
return `$${value.toFixed(value > 0 && value < 0.01 ? 4 : 2)}`
}
function formatChange(value: number) {
if (value > 0) return `+${value}%`
return `${value}%`
}
function formatCatalogLimit(value: number | undefined) {
return value === undefined ? "Unknown" : formatTokens(value)
}
function formatCatalogModalities(value: string[]) {
if (value.length === 0) return "Unknown"
return value.map(formatCatalogModality).join(", ")
}
function formatCatalogModality(value: string) {
if (value === "pdf") return "PDF"
return value.charAt(0).toUpperCase() + value.slice(1)
}
function formatCatalogDate(value: string | undefined) {
if (!value) return "Unknown"
const match = /^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?$/.exec(value)
if (!match) return value
const year = Number(match[1])
const month = match[2] ? Number(match[2]) - 1 : 0
const day = match[3] ? Number(match[3]) : 1
return new Intl.DateTimeFormat("en", {
month: match[2] ? "short" : undefined,
day: match[3] ? "numeric" : undefined,
year: "numeric",
timeZone: "UTC",
}).format(new Date(Date.UTC(year, month, day)))
}
function trimNumber(value: number, digits: number) {
return Number(value.toFixed(digits)).toLocaleString("en")
}
function providerSlug(provider: string) {
return provider
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.replace(/-{2,}/g, "-")
}

View File

@@ -0,0 +1,432 @@
import "../index.css"
import { Link, Meta, Title } from "@solidjs/meta"
import {
getStatsLabData,
type LabUsageModelEntry,
type ModelUsagePoint,
type StatsLabData,
} from "@opencode-ai/stats-core/domain/home"
import { runtime } from "@opencode-ai/stats-core/runtime"
import { createAsync, query, useParams } from "@solidjs/router"
import { createMemo, createSignal, For, onMount, Show, type JSX } from "solid-js"
import { getRequestEvent } from "solid-js/web"
import {
findModelCatalogLab,
formatCatalogLabName,
getModelCatalog,
type ModelCatalogEntry,
type ModelCatalogLab,
} from "../model-catalog"
import {
applyThemePreference,
Footer,
getGitHubStars,
Header,
isThemePreference,
themeStorageKey,
type HeaderLink,
type ThemePreference,
} from "../stats-shell"
const statsCanonicalBaseUrl = "https://opencode.ai/data/"
const statsUnfurlPath = "banner.png"
const statsUnfurlAlt = "OpenCode Data wordmark on a dark patterned background"
const statsUnfurlUrl = new URL(statsUnfurlPath, statsCanonicalBaseUrl).toString()
const labHeaderLinks: readonly HeaderLink[] = [
{ href: "#overview", label: "Overview" },
{ href: "#usage", label: "Usage" },
{ href: "#models", label: "Models" },
]
const labFooterLinks: readonly HeaderLink[] = [
{ href: import.meta.env.BASE_URL, label: "Data Home" },
{ href: `${import.meta.env.BASE_URL}#top-models`, label: "Top Models" },
{ href: `${import.meta.env.BASE_URL}#market-share`, label: "Market Share" },
{ href: `${import.meta.env.BASE_URL}#geo-breakdown`, label: "Geo Breakdown" },
]
const getLabData = query(async (lab: string) => {
"use server"
return runtime.runPromise(getStatsLabData(lab))
}, "getStatsLabData")
export default function StatsLab() {
const event = getRequestEvent()
event?.response.headers.set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=86400")
const params = useParams()
const labParam = createMemo(() => params.lab ?? "")
const catalog = createAsync(() => getModelCatalog())
const lab = createMemo(() => {
const data = catalog()
if (!data) return undefined
return findModelCatalogLab(data, labParam()) ?? null
})
const stats = createAsync(() => {
const entry = lab()
if (catalog() === undefined || entry === undefined) return Promise.resolve(undefined)
if (!entry) return Promise.resolve(null)
return getLabData(entry.id)
})
const githubStars = createAsync(() => getGitHubStars())
const [themePreference, setThemePreference] = createSignal<ThemePreference>("system")
const labName = createMemo(() => lab()?.name ?? formatCatalogLabName(labParam()))
const labTitle = createMemo(() => `${labName()} Models`)
const labDescription = createMemo(
() =>
`Explore ${labName()} models used in OpenCode, with recent token usage, context windows, release dates, and model-specific data.`,
)
const labUrl = createMemo(() => new URL(lab()?.id ?? labParam(), statsCanonicalBaseUrl).toString())
const updateThemePreference = (preference: ThemePreference) => {
applyThemePreference(preference)
setThemePreference(preference)
if (typeof window === "undefined") return
window.localStorage.setItem(themeStorageKey, preference)
}
onMount(() => {
if (typeof window === "undefined") return
const preference = window.localStorage.getItem(themeStorageKey)
const nextPreference = isThemePreference(preference) ? preference : "system"
applyThemePreference(nextPreference)
setThemePreference(nextPreference)
})
return (
<main data-page="stats" data-theme={themePreference()}>
<Title>{labTitle()}</Title>
<Meta name="description" content={labDescription()} />
<Link rel="canonical" href={labUrl()} />
<Meta property="og:type" content="website" />
<Meta property="og:site_name" content="OpenCode" />
<Meta property="og:title" content={labTitle()} />
<Meta property="og:description" content={labDescription()} />
<Meta property="og:url" content={labUrl()} />
<Meta property="og:image" content={statsUnfurlUrl} />
<Meta property="og:image:type" content="image/png" />
<Meta property="og:image:width" content="1200" />
<Meta property="og:image:height" content="630" />
<Meta property="og:image:alt" content={statsUnfurlAlt} />
<Meta name="twitter:card" content="summary_large_image" />
<Meta name="twitter:title" content={labTitle()} />
<Meta name="twitter:description" content={labDescription()} />
<Meta name="twitter:image" content={statsUnfurlUrl} />
<Meta name="twitter:image:alt" content={statsUnfurlAlt} />
<Header githubStars={githubStars() ?? "150K"} links={labHeaderLinks} brandHref={import.meta.env.BASE_URL} />
<div data-component="container">
<div data-component="content">
<Show when={catalog() !== undefined} fallback={<LabLoading />}>
<Show when={lab()} fallback={<LabNotFound lab={labParam()} />}>
{(data) => (
<>
<LabHero lab={data()} stats={stats() ?? null} />
<LabUsageSection lab={data()} data={stats() ?? null} />
<LabModelsSection lab={data()} usage={stats()?.models ?? []} />
</>
)}
</Show>
</Show>
</div>
<Footer
themePreference={themePreference()}
onThemePreferenceChange={updateThemePreference}
links={labFooterLinks}
/>
</div>
</main>
)
}
function LabLoading() {
return (
<section id="overview" data-section="lab-hero">
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<a data-slot="model-back-link" href={import.meta.env.BASE_URL}>
Data
</a>
<h1>Model Lab</h1>
<p>Reading model availability and recent OpenCode usage.</p>
</div>
</div>
</section>
)
}
function LabNotFound(props: { lab: string }) {
return (
<section id="overview" data-section="lab-hero">
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<a data-slot="model-back-link" href={import.meta.env.BASE_URL}>
Data
</a>
<h1>{formatCatalogLabName(props.lab)}</h1>
<p>No models matched this lab.</p>
</div>
</div>
</section>
)
}
function LabHero(props: { lab: ModelCatalogLab; stats: StatsLabData | null }) {
const latest = createMemo(
() =>
props.lab.models
.map((model) => model.releaseDate)
.filter((value): value is string => value !== undefined)
.toSorted((a, b) => new Date(b).getTime() - new Date(a).getTime())[0],
)
const featuredModels = createMemo(() => props.lab.models.slice(0, 3).map((model) => model.name))
return (
<section id="overview" data-section="lab-hero">
<a data-slot="model-back-link" href={import.meta.env.BASE_URL}>
Data
</a>
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<h1>{props.lab.name}</h1>
<div data-slot="model-hero-pattern" aria-hidden="true" />
<p>
Explore {props.lab.models.length} {props.lab.name} models used in OpenCode
<Show when={featuredModels().length > 0}> including {formatList(featuredModels())}</Show>. Compare recent
token usage, context windows, release dates, and model-specific data.
</p>
</div>
<div data-component="model-rank-panel">
<span>Tokens Processed</span>
<strong>{props.stats ? formatTokens(props.stats.totals.tokens) : "Pending"}</strong>
<p>
{props.stats
? `${formatPercent(props.stats.tokenShare)} of recent OpenCode usage`
: latest()
? `Latest release ${formatCatalogDate(latest())}`
: "Usage appears after model activity lands"}
</p>
</div>
</div>
</section>
)
}
function LabUsageSection(props: { lab: ModelCatalogLab; data: StatsLabData | null }) {
const [activeIndex, setActiveIndex] = createSignal<number>()
const usage = createMemo(() => props.data?.usage ?? [])
const max = createMemo(() => Math.max(0, ...usage().map((item) => item.tokens)) || 1)
const activePoint = createMemo(() => {
const index = activeIndex()
if (index === undefined) return undefined
return usage()[index]
})
return (
<section id="usage" data-section="model-panel">
<p data-slot="section-title">
<strong>{props.lab.name} token usage.</strong>{" "}
<span>Daily OpenCode token volume over the last two months.</span>
</p>
<Show
when={usage().some((item) => item.tokens > 0)}
fallback={
<LabEmptyState
title="No usage yet"
description="Recent token usage appears here once this lab has activity."
/>
}
>
<div
data-component="model-usage-chart"
data-dense-labels={isLabUsageDense(usage().length) ? "true" : undefined}
role="img"
aria-label={`${props.lab.name} daily token usage chart`}
style={{ "--model-usage-count": usage().length } as JSX.CSSProperties}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(undefined)
}}
>
<div data-slot="model-usage-axis" aria-hidden="true">
<For each={usage()}>
{(point, index) => (
<div
data-active={activeIndex() === index() ? "true" : undefined}
data-label-hidden={isLabUsageLabelHidden(index(), usage().length) ? "true" : undefined}
>
<span data-slot="model-usage-label">
<span data-slot="model-usage-total">{formatTokens(point.tokens)}</span>
<span data-slot="model-usage-date">{point.date}</span>
</span>
</div>
)}
</For>
</div>
<div data-slot="model-usage-bars">
<For each={usage()}>
{(point, index) => (
<div
data-slot="model-usage-column"
role="button"
tabIndex={0}
aria-label={`${point.date} ${formatTokens(point.tokens)} tokens`}
data-active={activeIndex() === index() ? "true" : undefined}
data-muted={activeIndex() !== undefined && activeIndex() !== index() ? "true" : undefined}
onPointerDown={(event) => {
if (event.pointerType !== "touch") return
setActiveIndex(index())
}}
onPointerEnter={() => setActiveIndex(index())}
onPointerMove={(event) => {
if (event.pointerType === "touch") return
setActiveIndex(index())
}}
onClick={() => setActiveIndex(index())}
onFocus={() => setActiveIndex(index())}
onBlur={() => setActiveIndex(undefined)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return
event.preventDefault()
setActiveIndex(index())
}}
>
<div
data-slot="model-usage-bar"
style={{ "--model-usage-fill": `${usageHeight(point.tokens, max())}%` } as JSX.CSSProperties}
/>
<Show when={activeIndex() === index() && activePoint()}>
{(active) => (
<div
data-component="chart-tooltip"
data-placement={index() > usage().length * 0.62 ? "left" : "right"}
>
<strong>{active().date}</strong>
<span>{formatTokens(active().tokens)} tokens</span>
<div data-slot="tooltip-divider" />
<p>
<span data-slot="tooltip-label">
<i /> Daily tokens
</span>
<b>{formatTokens(active().tokens)}</b>
</p>
</div>
)}
</Show>
</div>
)}
</For>
</div>
</div>
</Show>
</section>
)
}
function LabModelsSection(props: { lab: ModelCatalogLab; usage: LabUsageModelEntry[] }) {
const usageBySlug = createMemo(() => new Map(props.usage.map((item) => [item.slug, item])))
return (
<section id="models" data-section="model-panel">
<p data-slot="section-title">
<strong>{props.lab.name} models.</strong> <span>Recent usage and limits.</span>
</p>
<div data-component="lab-model-grid">
<For each={props.lab.models}>
{(model) => <LabModelCard model={model} usage={usageBySlug().get(model.slug)} />}
</For>
</div>
</section>
)
}
function LabModelCard(props: { model: ModelCatalogEntry; usage: LabUsageModelEntry | undefined }) {
return (
<a data-component="lab-model-card" href={`${import.meta.env.BASE_URL}${props.model.id}`}>
<strong>{props.model.name}</strong>
<div data-slot="lab-model-card-meta">
<p>
<b>Usage</b>
<em>{props.usage ? formatTokens(props.usage.tokens) : "—"}</em>
</p>
<p>
<b>Share</b>
<em>{props.usage ? formatPercent(props.usage.share) : "—"}</em>
</p>
<p>
<b>Context</b>
<em>{formatCatalogLimit(props.model.limit?.context)}</em>
</p>
<p>
<b>Output</b>
<em>{formatCatalogLimit(props.model.limit?.output)}</em>
</p>
<p>
<b>Release</b>
<em>{formatCatalogDate(props.model.releaseDate)}</em>
</p>
</div>
</a>
)
}
function LabEmptyState(props: { title: string; description: string }) {
return (
<div data-component="empty-state" data-compact="true">
<strong>{props.title}</strong>
<p>{props.description}</p>
</div>
)
}
function formatCatalogLimit(value: number | undefined) {
return value === undefined ? "Unknown" : formatTokens(value)
}
function formatCatalogDate(value: string | undefined) {
if (!value) return "Unknown"
const match = /^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?$/.exec(value)
if (!match) return value
const year = Number(match[1])
const month = match[2] ? Number(match[2]) - 1 : 0
const day = match[3] ? Number(match[3]) : 1
return new Intl.DateTimeFormat("en", {
month: match[2] ? "short" : undefined,
day: match[3] ? "numeric" : undefined,
year: "numeric",
timeZone: "UTC",
}).format(new Date(Date.UTC(year, month, day)))
}
function formatList(values: string[]) {
if (values.length <= 1) return values[0] ?? ""
if (values.length === 2) return `${values[0]} and ${values[1]}`
return `${values.slice(0, -1).join(", ")}, and ${values[values.length - 1]}`
}
function formatPercent(value: number) {
return `${trimNumber(value, value >= 10 ? 1 : 2)}%`
}
function formatTokens(value: number) {
if (value >= 1_000_000_000_000)
return `${trimNumber(value / 1_000_000_000_000, value >= 10_000_000_000_000 ? 0 : 1)}T`
if (value >= 1_000_000_000) return `${trimNumber(value / 1_000_000_000, value >= 10_000_000_000 ? 0 : 1)}B`
if (value >= 1_000_000) return `${trimNumber(value / 1_000_000, value >= 10_000_000 ? 0 : 1)}M`
if (value >= 1_000) return `${trimNumber(value / 1_000, value >= 10_000 ? 0 : 1)}K`
return String(Math.round(value))
}
function trimNumber(value: number, digits: number) {
return Number(value.toFixed(digits)).toLocaleString("en")
}
function usageHeight(value: number, max: number) {
if (value <= 0 || max <= 0) return 0
return Math.max(4, (value / max) * 100)
}
function isLabUsageDense(count: number) {
return count > 20
}
function isLabUsageLabelHidden(index: number, count: number) {
if (count <= 14) return false
const cadence = count > 45 ? 7 : count > 28 ? 4 : 2
return index % cadence !== 0 && index !== count - 1
}

View File

@@ -0,0 +1,19 @@
import { AppConfig } from "@opencode-ai/stats-core/config"
import { runtime } from "@opencode-ai/stats-core/runtime"
import { Effect } from "effect"
export async function GET() {
return Response.json(
await runtime.runPromise(
Effect.gen(function* () {
const config = yield* AppConfig
return {
ok: true,
app: "stats",
stage: config.stage,
publicUrl: config.publicUrl,
}
}),
),
)
}

View File

@@ -0,0 +1,29 @@
import { Resource } from "sst/resource"
const listId = "8b9bb82c-9d5f-11f0-975f-0df6fd1e4945"
export async function POST(event: { request: Request }) {
const contentType = event.request.headers.get("content-type") ?? ""
if (!contentType.includes("multipart/form-data") && !contentType.includes("application/x-www-form-urlencoded")) {
return Response.json({ error: "Email address is required" }, { status: 400 })
}
const form = await event.request.formData()
const emailAddress = form.get("email")
if (typeof emailAddress !== "string" || emailAddress.trim().length === 0) {
return Response.json({ error: "Email address is required" }, { status: 400 })
}
const response = await fetch(`https://api.emailoctopus.com/lists/${listId}/contacts`, {
method: "PUT",
headers: {
Authorization: `Bearer ${Resource.EMAILOCTOPUS_API_KEY.value}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
email_address: emailAddress.trim(),
}),
})
if (!response.ok) return Response.json({ error: "Failed to subscribe" }, { status: 502 })
return Response.json({ success: true })
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,224 @@
import { query } from "@solidjs/router"
export const modelCatalogSourceUrl = "https://models.dev/models.json"
export type ModelCatalogEntry = {
id: string
lab: string
slug: string
name: string
family?: string
knowledge?: string
releaseDate?: string
lastUpdated?: string
limit?: { context?: number; output?: number }
modalities: { input: string[]; output: string[] }
openWeights: boolean
reasoning: boolean
toolCall: boolean
attachment: boolean
temperature: boolean
weights: { label: string; url: string }[]
benchmarks: ModelCatalogBenchmark[]
}
export type ModelCatalogBenchmark = {
name: string
score: number
metric?: string
harness?: string
variant?: string
dataset?: string
version?: string
source?: string
}
export type ModelCatalogLab = {
id: string
name: string
models: ModelCatalogEntry[]
}
export type ModelCatalog = {
models: ModelCatalogEntry[]
labs: ModelCatalogLab[]
}
export const getModelCatalog = query(async () => {
"use server"
const payload = await fetch(modelCatalogSourceUrl)
.then((response): Promise<unknown> => (response.ok ? (response.json() as Promise<unknown>) : Promise.resolve()))
.catch(() => undefined)
return buildModelCatalog(payload)
}, "getModelCatalog")
export function findModelCatalogEntry(catalog: ModelCatalog, model: string, lab?: string) {
const normalizedId = lab ? `${catalogSlug(lab)}/${catalogSlug(model)}` : model.trim().toLowerCase()
const leaf = catalogSlug(model)
return (
catalog.models.find((entry) => entry.id.toLowerCase() === normalizedId) ??
catalog.models.find((entry) => (lab ? entry.lab === catalogSlug(lab) : true) && entry.slug === leaf) ??
catalog.models.find((entry) => entry.slug === leaf)
)
}
export function findModelCatalogLab(catalog: ModelCatalog, lab: string) {
const id = catalogSlug(lab)
return catalog.labs.find((entry) => entry.id === id)
}
export function formatCatalogLabName(lab: string) {
const known: Record<string, string> = {
alibaba: "Alibaba",
anthropic: "Anthropic",
cohere: "Cohere",
deepseek: "DeepSeek",
google: "Google",
meta: "Meta",
minimax: "MiniMax",
mistral: "Mistral",
moonshotai: "Moonshot",
openai: "OpenAI",
perplexity: "Perplexity",
stepfun: "StepFun",
tencent: "Tencent",
xai: "xAI",
xiaomi: "Xiaomi",
zai: "Z.ai",
zhipuai: "Zhipu",
}
return known[catalogSlug(lab)] ?? lab.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase())
}
export function catalogSlug(value: string) {
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.replace(/-{2,}/g, "-")
}
function buildModelCatalog(payload: unknown): ModelCatalog {
const models = (Array.isArray(payload) ? payload : isRecord(payload) ? Object.values(payload) : [])
.flatMap(readModelCatalogEntry)
.toSorted((a, b) => a.lab.localeCompare(b.lab) || displayDateTime(b.releaseDate) - displayDateTime(a.releaseDate))
return {
models,
labs: Object.values(
models.reduce<Record<string, ModelCatalogLab>>((result, model) => {
result[model.lab] = {
id: model.lab,
name: formatCatalogLabName(model.lab),
models: [...(result[model.lab]?.models ?? []), model],
}
return result
}, {}),
).toSorted((a, b) => a.name.localeCompare(b.name)),
}
}
function readModelCatalogEntry(value: unknown): ModelCatalogEntry[] {
if (!isRecord(value)) return []
const id = stringValue(value.id)
const name = stringValue(value.name)
const lab = id?.split("/")[0]
const slug = id?.split("/").slice(1).join("/")
if (!id || !name || !lab || !slug) return []
return [
{
id,
lab: catalogSlug(lab),
slug: catalogSlug(slug),
name,
family: stringValue(value.family),
knowledge: stringValue(value.knowledge),
releaseDate: stringValue(value.release_date),
lastUpdated: stringValue(value.last_updated),
limit: readCatalogLimit(value.limit),
modalities: readCatalogModalities(value.modalities),
openWeights: booleanValue(value.open_weights),
reasoning: booleanValue(value.reasoning),
toolCall: booleanValue(value.tool_call),
attachment: booleanValue(value.attachment),
temperature: booleanValue(value.temperature),
weights: readCatalogWeights(value.weights),
benchmarks: readCatalogBenchmarks(value.benchmarks),
},
]
}
function readCatalogLimit(value: unknown) {
if (!isRecord(value)) return undefined
return {
context: numberValue(value.context),
output: numberValue(value.output),
}
}
function readCatalogModalities(value: unknown) {
if (!isRecord(value)) return { input: [], output: [] }
return {
input: stringArrayValue(value.input),
output: stringArrayValue(value.output),
}
}
function readCatalogWeights(value: unknown) {
if (!Array.isArray(value)) return []
return value.flatMap((item) => {
if (!isRecord(item)) return []
const label = stringValue(item.label)
const url = stringValue(item.url)
return label && url ? [{ label, url }] : []
})
}
function readCatalogBenchmarks(value: unknown) {
if (!Array.isArray(value)) return []
return value.flatMap((item) => {
if (!isRecord(item)) return []
const name = stringValue(item.name)
const score = numberValue(item.score)
return name && score !== undefined
? [
{
name,
score,
metric: stringValue(item.metric),
harness: stringValue(item.harness),
variant: stringValue(item.variant),
dataset: stringValue(item.dataset),
version: stringValue(item.version),
source: stringValue(item.source),
},
]
: []
})
}
function displayDateTime(value: string | undefined) {
return value ? new Date(value).getTime() || 0 : 0
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
}
function stringValue(value: unknown) {
return typeof value === "string" && value.trim() ? value : undefined
}
function numberValue(value: unknown) {
return typeof value === "number" && Number.isFinite(value) ? value : undefined
}
function booleanValue(value: unknown) {
return value === true
}
function stringArrayValue(value: unknown) {
return Array.isArray(value)
? value.filter((item): item is string => typeof item === "string" && item.trim() !== "")
: []
}

View File

@@ -0,0 +1,476 @@
import opencodeWordmarkDark from "../asset/logo-ornate-dark.svg"
import { query } from "@solidjs/router"
import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js"
export type HeaderLink = { href: string; label: string }
export const headerLinks = [
{ href: "#top-models", label: "Top Models" },
{ href: "#leaderboard", label: "Leaderboard" },
{ href: "#session-cost", label: "Session Cost" },
{ href: "#token-cost", label: "Token Cost" },
{ href: "#cache-ratio", label: "Cache Ratio" },
{ href: "#market-share", label: "Market Share" },
{ href: "#geo-breakdown", label: "Geo Breakdown" },
] as const
export const githubLink = {
href: "https://github.com/anomalyco/opencode",
apiHref: "https://api.github.com/repos/anomalyco/opencode",
label: "GitHub",
fallbackStars: "150K",
ariaLabel: "Star OpenCode on GitHub",
}
export const themePreferences = ["dark", "light", "system"] as const
export const themeStorageKey = "opencode:stats-theme"
export type ThemePreference = (typeof themePreferences)[number]
const compactNumberFormatter = new Intl.NumberFormat("en", {
notation: "compact",
maximumFractionDigits: 1,
})
const themePreferenceLabels = {
dark: "Dark",
light: "Light",
system: "System",
} as const
export const getGitHubStars = query(async () => {
"use server"
return fetch(githubLink.apiHref, {
headers: {
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
})
.then((response) => (response.ok ? response.json() : undefined))
.then((body: unknown) =>
body && typeof body === "object" && "stargazers_count" in body && typeof body.stargazers_count === "number"
? compactNumberFormatter.format(body.stargazers_count)
: githubLink.fallbackStars,
)
.catch(() => githubLink.fallbackStars)
}, "getGitHubStars")
export function isThemePreference(value: string | null): value is ThemePreference {
return value === "dark" || value === "light" || value === "system"
}
export function applyThemePreference(preference: ThemePreference) {
if (typeof document === "undefined") return
document.documentElement.dataset.statsTheme = preference
if (preference === "system") {
document.documentElement.style.removeProperty("color-scheme")
return
}
document.documentElement.style.setProperty("color-scheme", preference)
}
export function Header(props: { githubStars: string; links?: readonly HeaderLink[]; brandHref?: string }) {
const [menuOpen, setMenuOpen] = createSignal(false)
const [menuViewport, setMenuViewport] = createSignal(false)
const links = createMemo(() => props.links ?? headerLinks)
createEffect(() => {
if (typeof window === "undefined") return
const media = window.matchMedia("(max-width: 74.999rem)")
const update = () => setMenuViewport(media.matches)
update()
media.addEventListener("change", update)
onCleanup(() => media.removeEventListener("change", update))
})
createEffect(() => {
if (!menuOpen()) return
if (!menuViewport()) return
if (typeof document === "undefined") return
const page = document.querySelector<HTMLElement>('[data-page="stats"]')
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
const htmlOverflow = document.documentElement.style.overflow
const pagePaddingRight = page?.style.paddingRight
const bodyOverflow = document.body.style.overflow
document.documentElement.style.overflow = "hidden"
if (scrollbarWidth > 0 && page) page.style.paddingRight = `${scrollbarWidth}px`
document.body.style.overflow = "hidden"
onCleanup(() => {
document.documentElement.style.overflow = htmlOverflow
if (page && pagePaddingRight !== undefined) page.style.paddingRight = pagePaddingRight
document.body.style.overflow = bodyOverflow
})
})
return (
<header data-component="top" data-menu-open={menuOpen() ? "true" : undefined}>
<div data-slot="header-bar">
<a data-slot="brand" href={props.brandHref ?? import.meta.env.BASE_URL} aria-label="Data home">
<DataWordmark />
</a>
<nav data-component="section-nav" aria-label="Data sections">
<ul>
<For each={links()}>
{(link) => (
<li>
<a href={link.href}>{link.label}</a>
</li>
)}
</For>
</ul>
</nav>
<div data-slot="header-actions">
<a
data-slot="header-button"
data-variant="neutral"
href={githubLink.href}
target="_blank"
rel="noreferrer"
aria-label={`${githubLink.ariaLabel} (${props.githubStars} stars)`}
>
<strong>{githubLink.label}</strong>
<span>[{props.githubStars}]</span>
</a>
<a data-slot="header-button" data-variant="contrast" href="https://opencode.ai/">
<strong>Try OpenCode</strong>
</a>
<button
data-slot="menu-button"
type="button"
aria-controls="stats-mobile-nav"
aria-expanded={menuOpen() ? "true" : "false"}
aria-label={menuOpen() ? "Close navigation" : "Open navigation"}
onClick={() => setMenuOpen((value) => !value)}
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<Show when={menuOpen()} fallback={<path d="M2 4.72H14M2 8.5H14M2 12.28H14" stroke="currentColor" />}>
<path d="M4.44 4.44L11.56 11.56M11.56 4.44L4.44 11.56" stroke="currentColor" />
</Show>
</svg>
</button>
</div>
</div>
<nav id="stats-mobile-nav" data-slot="mobile-menu" aria-label="Data sections" hidden={!menuOpen()}>
<a
data-slot="mobile-menu-item"
data-variant="github"
href={githubLink.href}
target="_blank"
rel="noreferrer"
aria-label={`${githubLink.ariaLabel} (${props.githubStars} stars)`}
>
<strong>{githubLink.label}</strong>
<span>[{props.githubStars}]</span>
</a>
<For each={links()}>
{(link) => (
<a data-slot="mobile-menu-item" href={link.href} onClick={() => setMenuOpen(false)}>
{link.label}
</a>
)}
</For>
</nav>
</header>
)
}
function DataWordmark() {
return (
<svg data-slot="stats-wordmark" width="66" height="20" viewBox="0 0 66 20" fill="none" aria-hidden="true">
<path opacity="0.2" d="M12 16H4V8H12V16Z" fill="currentColor" />
<path d="M12 4H4V16H12V4ZM16 20H0V0H16V20Z" fill="currentColor" />
<path
d="M63.3543 16L62.5119 12.8711H58.6437L57.8013 16H55.7383L59.2454 4H61.9618L65.4689 16H63.3543ZM61.0678 7.851L60.6896 5.94269H60.4489L60.0707 7.851L59.1595 11.1347H61.9962L61.0678 7.851Z"
fill="currentColor"
/>
<path d="M52.5951 5.87392V16H50.4461V5.87392H47.4375V4H55.6209V5.87392H52.5951Z" fill="currentColor" />
<path
d="M45.2059 16L44.3635 12.8711H40.4953L39.6529 16H37.5898L41.097 4H43.8133L47.3205 16H45.2059ZM42.9194 7.851L42.5411 5.94269H42.3004L41.9222 7.851L41.011 11.1347H43.8477L42.9194 7.851Z"
fill="currentColor"
/>
<path
d="M28 4H32.0917C32.8138 4 33.4556 4.11461 34.0172 4.34384C34.5903 4.5616 35.0716 4.9169 35.4613 5.40974C35.8625 5.89112 36.1662 6.51003 36.3725 7.26648C36.5788 8.02292 36.6819 8.9341 36.6819 10C36.6819 11.0659 36.5788 11.9771 36.3725 12.7335C36.1662 13.49 35.8625 14.1146 35.4613 14.6075C35.0716 15.0888 34.5903 15.4441 34.0172 15.6734C33.4556 15.8911 32.8138 16 32.0917 16H28V4ZM32.0917 14.1261C32.8252 14.1261 33.3926 13.9026 33.7937 13.4556C34.1948 12.9971 34.3954 12.3152 34.3954 11.4097V8.59026C34.3954 7.68481 34.1948 7.0086 33.7937 6.5616C33.3926 6.10315 32.8252 5.87392 32.0917 5.87392H30.149V14.1261H32.0917Z"
fill="currentColor"
/>
</svg>
)
}
function OpenCodeMark() {
return (
<svg data-slot="opencode-mark" width="40" height="40" viewBox="0 0 40 40" fill="none" aria-hidden="true">
<path d="M40 40H0V0H40V40Z" fill="var(--stats-logo-bg)" />
<path d="M26 29H14V17H26V29Z" fill="var(--stats-logo-fill)" />
<path d="M26 11H14V29H26V11ZM32 35H8V5H32V35Z" fill="var(--stats-logo-stroke)" />
</svg>
)
}
export function Footer(props: {
themePreference: ThemePreference
onThemePreferenceChange: (preference: ThemePreference) => void
links?: readonly HeaderLink[]
}) {
const [subscribeOpen, setSubscribeOpen] = createSignal(false)
const modelStats = props.links ?? [
{ href: "#top-models", label: "Top Models" },
{ href: "#leaderboard", label: "Leaderboard" },
{ href: "#session-cost", label: "Session Cost" },
{ href: "#token-cost", label: "Token Cost" },
{ href: "#cache-ratio", label: "Cache Ratio" },
{ href: "#market-share", label: "Market Share" },
{ href: "#geo-breakdown", label: "Geo Breakdown" },
]
const legal = [
{ href: "https://opencode.ai/legal/terms-of-service", label: "Terms of service" },
{ href: "https://opencode.ai/legal/privacy-policy", label: "Privacy policy" },
]
const connect = [
{ href: "mailto:hello@opencode.ai", label: "Contact us" },
{ href: "https://opencode.ai/discord", label: "Community" },
{ href: "https://x.com/opencode", label: "X" },
githubLink,
{ href: "https://www.youtube.com/@anomaly-co", label: "YouTube" },
]
return (
<footer data-component="footer">
<SectionBridge label="GEO BREAKDOWN" href="#geo-breakdown" />
<div data-slot="footer-grid">
<a data-slot="footer-mark" href="https://opencode.ai" aria-label="OpenCode home">
<OpenCodeMark />
</a>
<FooterColumn title="Model Data" links={modelStats} />
<FooterColumn title="Legal" links={legal} />
<FooterColumn title="Connect" links={connect} />
<div data-slot="footer-column">
<h2>Newsletter</h2>
<p>Be the first to know about new releases.</p>
<button data-slot="subscribe-button" type="button" onClick={() => setSubscribeOpen(true)}>
Subscribe
</button>
</div>
</div>
<div data-slot="footer-pattern" aria-hidden="true" />
<div data-slot="footer-bottom">
<div>
<span>© 2026 Anomaly Innovations Inc.</span>
<span data-slot="status">All systems Operational</span>
</div>
<div data-slot="theme-toggle" role="group" aria-label="Theme">
<For each={themePreferences}>
{(preference) => (
<button
data-slot="theme-option"
type="button"
aria-label={themePreferenceLabels[preference]}
aria-pressed={props.themePreference === preference ? "true" : "false"}
title={themePreferenceLabels[preference]}
onClick={() => props.onThemePreferenceChange(preference)}
>
<ThemePreferenceIcon preference={preference} />
</button>
)}
</For>
</div>
</div>
<Show when={subscribeOpen()}>
<SubscribeModal onClose={() => setSubscribeOpen(false)} />
</Show>
</footer>
)
}
function SectionBridge(props: { label: string; href: string }) {
return (
<a data-component="section-bridge" href={props.href}>
<span>LEAN MORE</span>
<i />
<strong>{props.label}</strong>
<b></b>
</a>
)
}
function ThemePreferenceIcon(props: { preference: ThemePreference }) {
return (
<svg data-slot="theme-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<Show
when={props.preference === "dark"}
fallback={
<Show
when={props.preference === "light"}
fallback={
<>
<rect x="1.5552" y="2.4448" width="12.8896" height="8.8888" fill="currentColor" opacity="0.3" />
<svg
x="1.0552"
y="1.9446"
width="13.8889"
height="12.5325"
viewBox="0 0 13.8889 12.5325"
preserveAspectRatio="none"
overflow="visible"
>
<path
d="M4.05559 12.0555C4.72936 11.8431 5.72492 11.6111 6.94448 11.6111M6.94448 11.6111C7.65114 11.6111 8.66981 11.6893 9.83336 12.0555M6.94448 11.6111L6.94448 9.38888M13.3889 0.5H0.500102C0.500102 0.5 0.500017 1.29594 0.500017 2.27778V7.61112C0.500017 8.59298 0.500007 9.38889 0.500007 9.38889H13.3889C13.3889 9.38889 13.3889 8.59298 13.3889 7.61112V2.27778C13.3889 1.29594 13.3889 0.5 13.3889 0.5Z"
stroke="currentColor"
/>
</svg>
</>
}
>
<svg
x="0.6102"
y="0.6102"
width="14.7778"
height="14.7778"
viewBox="0 0 14.7778 14.7778"
preserveAspectRatio="none"
overflow="visible"
>
<path
d="M7.38889 0.5V1.38889M12.26 2.51782L11.6315 3.14627M14.2778 7.38892H13.3889M12.26 12.26L11.6315 11.6316M7.38889 14.2778V13.3889M2.51778 12.26L3.14622 11.6316M0.5 7.38892H1.38889M2.51778 2.51782L3.14622 3.14627M7.38888 11.1666C9.47528 11.1666 11.1667 9.47526 11.1667 7.38886C11.1667 5.30245 9.47528 3.61108 7.38888 3.61108C5.30247 3.61108 3.6111 5.30245 3.6111 7.38886C3.6111 9.47526 5.30247 11.1666 7.38888 11.1666Z"
stroke="currentColor"
stroke-linecap="square"
/>
</svg>
</Show>
}
>
<svg
x="2.0549"
y="1.742"
width="12.3867"
height="12.3971"
viewBox="0 0 12.3867 12.3971"
preserveAspectRatio="none"
overflow="visible"
>
<path
d="M9.05556 8.39711C6.37067 8.39711 4.19444 6.22089 4.19444 3.536C4.19444 2.48445 4.53122 1.51456 5.09822 0.71889C2.48178 1.20733 0.5 3.49944 0.5 6.25822C0.5 9.37244 3.02467 11.8971 6.13889 11.8971C8.76156 11.8971 10.9596 10.1036 11.5903 7.67844C10.8514 8.13189 9.98578 8.39711 9.05556 8.39711Z"
stroke="currentColor"
stroke-linecap="round"
/>
</svg>
</Show>
</svg>
)
}
function SubscribeModal(props: { onClose: () => void }) {
const [status, setStatus] = createSignal<"idle" | "pending" | "success" | "error">("idle")
const [message, setMessage] = createSignal("")
let input: HTMLInputElement | undefined
onMount(() => {
if (typeof document === "undefined") return
const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : undefined
const htmlOverflow = document.documentElement.style.overflow
const bodyOverflow = document.body.style.overflow
document.documentElement.style.overflow = "hidden"
document.body.style.overflow = "hidden"
const focusTimeout = window.setTimeout(() => input?.focus(), 0)
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") props.onClose()
}
document.addEventListener("keydown", onKeyDown)
onCleanup(() => {
window.clearTimeout(focusTimeout)
document.documentElement.style.overflow = htmlOverflow
document.body.style.overflow = bodyOverflow
document.removeEventListener("keydown", onKeyDown)
activeElement?.focus()
})
})
return (
<div data-component="subscribe-modal" role="dialog" aria-modal="true" aria-labelledby="subscribe-title">
<div data-slot="modal-scrim" aria-hidden="true" onClick={props.onClose} />
<div data-slot="modal-panel">
<div data-slot="modal-brand">
<img data-slot="modal-logo" src={opencodeWordmarkDark} alt="OpenCode" />
<button data-slot="modal-close" type="button" aria-label="Close newsletter signup" onClick={props.onClose}>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path d="M4.44 4.44L11.56 11.56M11.56 4.44L4.44 11.56" stroke="currentColor" />
</svg>
</button>
</div>
<div data-slot="modal-body">
<div data-slot="modal-intro">
<h2 id="subscribe-title">OpenCode Newsletter</h2>
<p>
Be the first to know
<br />
about new releases.
</p>
</div>
<form
data-slot="subscribe-form"
method="post"
onSubmit={(event) => {
event.preventDefault()
const form = event.currentTarget
setStatus("pending")
setMessage("")
fetch(`${import.meta.env.BASE_URL}api/newsletter`, {
method: "POST",
body: new FormData(form),
}).then(
async (response) => {
if (response.ok) {
form.reset()
setStatus("success")
return
}
setMessage(await newsletterErrorMessage(response))
setStatus("error")
},
() => {
setMessage("Failed to subscribe")
setStatus("error")
},
)
}}
>
<input ref={input} type="email" name="email" placeholder="Email address" required />
<button type="submit" disabled={status() === "pending"}>
<span>{status() === "pending" ? "Subscribing..." : "Subscribe"}</span>
</button>
</form>
<div data-slot="subscribe-feedback" aria-live="polite">
<Show when={status() === "success"}>
<p data-state="success">You're subscribed.</p>
</Show>
<Show when={status() === "error"}>
<p data-state="error">{message()}</p>
</Show>
</div>
</div>
</div>
</div>
)
}
function newsletterErrorMessage(response: Response) {
return response.json().then(
(body: unknown) =>
body && typeof body === "object" && "error" in body && typeof body.error === "string"
? body.error
: "Failed to subscribe",
() => "Failed to subscribe",
)
}
function FooterColumn(props: { title: string; links: readonly { href: string; label: string }[] }) {
return (
<div data-slot="footer-column">
<h2>{props.title}</h2>
<nav aria-label={props.title}>
<For each={props.links}>
{(link) => (
<a href={link.href} target={link.href.startsWith("http") ? "_blank" : undefined} rel="noreferrer">
{link.label}
</a>
)}
</For>
</nav>
</div>
)
}

View File

@@ -0,0 +1 @@
export { GET } from "../../api/health"

View File

@@ -0,0 +1 @@
export { POST } from "../../api/newsletter"

10
packages/stats/app/sst-env.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}

View File

@@ -0,0 +1,22 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"allowJs": true,
"strict": true,
"noEmit": true,
"types": ["vite/client", "bun"],
"isolatedModules": true,
"paths": {
"~/*": ["./src/*"]
}
},
"include": ["*.ts", "src", "../core/src/resource.d.ts"]
}

View File

@@ -0,0 +1,23 @@
import { solidStart } from "@solidjs/start/config"
import { nitro } from "nitro/vite"
import { defineConfig, type PluginOption } from "vite"
export default defineConfig({
base: "/data/",
plugins: [
solidStart() as PluginOption,
nitro({
compatibilityDate: "2024-09-19",
preset: "cloudflare-module",
cloudflare: {
nodeCompat: true,
},
}),
],
server: {
allowedHosts: true,
},
build: {
minify: false,
},
})

View File

@@ -0,0 +1,21 @@
import { Resource } from "sst/resource"
import { defineConfig } from "drizzle-kit"
export default defineConfig({
dialect: "mysql",
schema: ["./src/database/schema.ts"],
// schema: ["./src/**/*.sql.ts"],
out: "./migrations/",
strict: true,
verbose: true,
dbCredentials: {
database: Resource.StatsDatabase.database,
host: Resource.StatsDatabase.host,
user: Resource.StatsDatabase.username,
password: Resource.StatsDatabase.password,
port: Resource.StatsDatabase.port,
ssl: {
rejectUnauthorized: false,
},
},
})

View File

@@ -0,0 +1,42 @@
CREATE TABLE `stat` (
`id` bigint AUTO_INCREMENT PRIMARY KEY,
`grain` varchar(16) NOT NULL,
`period_start` datetime NOT NULL,
`period_end` datetime NOT NULL,
`dataset` varchar(64) NOT NULL DEFAULT 'all',
`tier` varchar(64) NOT NULL DEFAULT 'all',
`client` varchar(64) NOT NULL DEFAULT 'all',
`source` varchar(64) NOT NULL DEFAULT 'all',
`provider` varchar(128) NOT NULL,
`model` varchar(256) NOT NULL,
`provider_model` varchar(256) NOT NULL DEFAULT '',
`sessions` bigint NOT NULL DEFAULT 0,
`requests` bigint NOT NULL DEFAULT 0,
`input_tokens` bigint NOT NULL DEFAULT 0,
`output_tokens` bigint NOT NULL DEFAULT 0,
`reasoning_tokens` bigint NOT NULL DEFAULT 0,
`cache_read_tokens` bigint NOT NULL DEFAULT 0,
`total_tokens` bigint NOT NULL DEFAULT 0,
`input_cost_microcents` bigint NOT NULL DEFAULT 0,
`output_cost_microcents` bigint NOT NULL DEFAULT 0,
`total_cost_microcents` bigint NOT NULL DEFAULT 0,
`avg_duration_ms` decimal(12,2),
`p50_duration_ms` int,
`p95_duration_ms` int,
`avg_ttfb_ms` decimal(12,2),
`p50_ttfb_ms` int,
`p95_ttfb_ms` int,
`avg_output_tps` decimal(12,4),
`success_count` bigint NOT NULL DEFAULT 0,
`error_count` bigint NOT NULL DEFAULT 0,
`sample_count` bigint NOT NULL DEFAULT 0,
`rank_by_tokens` int,
`rank_by_requests` int,
`rank_by_cost` int,
`created_at` datetime NOT NULL DEFAULT (now()),
`updated_at` datetime NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `uniq_model_period` UNIQUE INDEX(`grain`,`period_start`,`dataset`,`tier`,`client`,`source`,`provider`,`model`)
);
--> statement-breakpoint
CREATE INDEX `idx_leaderboard_tokens` ON `stat` (`grain`,`period_start`,`dataset`,`tier`,`total_tokens`);--> statement-breakpoint
CREATE INDEX `idx_model` ON `stat` (`model`,`grain`,`period_start`);

View File

@@ -0,0 +1,623 @@
{
"version": "6",
"dialect": "mysql",
"id": "72655266-65da-408e-bfd8-9f3a4ad817a5",
"prevIds": ["00000000-0000-0000-0000-000000000000"],
"ddl": [
{
"name": "stat",
"entityType": "tables"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": true,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "id",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(16)",
"notNull": true,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "grain",
"entityType": "columns",
"table": "stat"
},
{
"type": "datetime",
"notNull": true,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "period_start",
"entityType": "columns",
"table": "stat"
},
{
"type": "datetime",
"notNull": true,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "period_end",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(64)",
"notNull": true,
"autoIncrement": false,
"default": "'all'",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "dataset",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(64)",
"notNull": true,
"autoIncrement": false,
"default": "'all'",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "tier",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(64)",
"notNull": true,
"autoIncrement": false,
"default": "'all'",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "client",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(64)",
"notNull": true,
"autoIncrement": false,
"default": "'all'",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "source",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(128)",
"notNull": true,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "provider",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(256)",
"notNull": true,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "model",
"entityType": "columns",
"table": "stat"
},
{
"type": "varchar(256)",
"notNull": true,
"autoIncrement": false,
"default": "''",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "provider_model",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "sessions",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "requests",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "input_tokens",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "output_tokens",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "reasoning_tokens",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "cache_read_tokens",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "total_tokens",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "input_cost_microcents",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "output_cost_microcents",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "total_cost_microcents",
"entityType": "columns",
"table": "stat"
},
{
"type": "decimal(12,2)",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "avg_duration_ms",
"entityType": "columns",
"table": "stat"
},
{
"type": "int",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "p50_duration_ms",
"entityType": "columns",
"table": "stat"
},
{
"type": "int",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "p95_duration_ms",
"entityType": "columns",
"table": "stat"
},
{
"type": "decimal(12,2)",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "avg_ttfb_ms",
"entityType": "columns",
"table": "stat"
},
{
"type": "int",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "p50_ttfb_ms",
"entityType": "columns",
"table": "stat"
},
{
"type": "int",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "p95_ttfb_ms",
"entityType": "columns",
"table": "stat"
},
{
"type": "decimal(12,4)",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "avg_output_tps",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "success_count",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "error_count",
"entityType": "columns",
"table": "stat"
},
{
"type": "bigint",
"notNull": true,
"autoIncrement": false,
"default": "0",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "sample_count",
"entityType": "columns",
"table": "stat"
},
{
"type": "int",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "rank_by_tokens",
"entityType": "columns",
"table": "stat"
},
{
"type": "int",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "rank_by_requests",
"entityType": "columns",
"table": "stat"
},
{
"type": "int",
"notNull": false,
"autoIncrement": false,
"default": null,
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "rank_by_cost",
"entityType": "columns",
"table": "stat"
},
{
"type": "datetime",
"notNull": true,
"autoIncrement": false,
"default": "(now())",
"onUpdateNow": false,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "created_at",
"entityType": "columns",
"table": "stat"
},
{
"type": "datetime",
"notNull": true,
"autoIncrement": false,
"default": "(now())",
"onUpdateNow": true,
"onUpdateNowFsp": null,
"charSet": null,
"collation": null,
"generated": null,
"name": "updated_at",
"entityType": "columns",
"table": "stat"
},
{
"columns": ["id"],
"name": "PRIMARY",
"table": "stat",
"entityType": "pks"
},
{
"columns": [
{
"value": "grain",
"isExpression": false
},
{
"value": "period_start",
"isExpression": false
},
{
"value": "dataset",
"isExpression": false
},
{
"value": "tier",
"isExpression": false
},
{
"value": "client",
"isExpression": false
},
{
"value": "source",
"isExpression": false
},
{
"value": "provider",
"isExpression": false
},
{
"value": "model",
"isExpression": false
}
],
"isUnique": true,
"using": null,
"algorithm": null,
"lock": null,
"nameExplicit": true,
"name": "uniq_model_period",
"entityType": "indexes",
"table": "stat"
},
{
"columns": [
{
"value": "grain",
"isExpression": false
},
{
"value": "period_start",
"isExpression": false
},
{
"value": "dataset",
"isExpression": false
},
{
"value": "tier",
"isExpression": false
},
{
"value": "total_tokens",
"isExpression": false
}
],
"isUnique": false,
"using": null,
"algorithm": null,
"lock": null,
"nameExplicit": true,
"name": "idx_leaderboard_tokens",
"entityType": "indexes",
"table": "stat"
},
{
"columns": [
{
"value": "model",
"isExpression": false
},
{
"value": "grain",
"isExpression": false
},
{
"value": "period_start",
"isExpression": false
}
],
"isUnique": false,
"using": null,
"algorithm": null,
"lock": null,
"nameExplicit": true,
"name": "idx_model",
"entityType": "indexes",
"table": "stat"
}
],
"renames": []
}

View File

@@ -0,0 +1,94 @@
CREATE TABLE `geo_stat` (
`id` bigint AUTO_INCREMENT PRIMARY KEY,
`grain` varchar(16) NOT NULL,
`period_start` datetime NOT NULL,
`period_end` datetime NOT NULL,
`dataset` varchar(64) NOT NULL DEFAULT 'all',
`tier` varchar(64) NOT NULL DEFAULT 'all',
`client` varchar(64) NOT NULL DEFAULT 'all',
`source` varchar(64) NOT NULL DEFAULT 'all',
`country` char(2) NOT NULL,
`continent` varchar(8) NOT NULL DEFAULT '',
`sessions` bigint NOT NULL DEFAULT 0,
`requests` bigint NOT NULL DEFAULT 0,
`input_tokens` bigint NOT NULL DEFAULT 0,
`output_tokens` bigint NOT NULL DEFAULT 0,
`reasoning_tokens` bigint NOT NULL DEFAULT 0,
`cache_read_tokens` bigint NOT NULL DEFAULT 0,
`total_tokens` bigint NOT NULL DEFAULT 0,
`input_cost_microcents` bigint NOT NULL DEFAULT 0,
`output_cost_microcents` bigint NOT NULL DEFAULT 0,
`total_cost_microcents` bigint NOT NULL DEFAULT 0,
`avg_duration_ms` decimal(12,2),
`p50_duration_ms` int,
`p95_duration_ms` int,
`avg_ttfb_ms` decimal(12,2),
`p50_ttfb_ms` int,
`p95_ttfb_ms` int,
`avg_output_tps` decimal(12,4),
`success_count` bigint NOT NULL DEFAULT 0,
`error_count` bigint NOT NULL DEFAULT 0,
`sample_count` bigint NOT NULL DEFAULT 0,
`market_share_tokens` decimal(10,6),
`market_share_requests` decimal(10,6),
`market_share_sessions` decimal(10,6),
`rank_by_tokens` int,
`rank_by_requests` int,
`rank_by_sessions` int,
`rank_by_cost` int,
`created_at` datetime NOT NULL DEFAULT (now()),
`updated_at` datetime NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `uniq_country_period` UNIQUE INDEX(`grain`,`period_start`,`dataset`,`tier`,`client`,`source`,`country`)
);
--> statement-breakpoint
CREATE TABLE `provider_stat` (
`id` bigint AUTO_INCREMENT PRIMARY KEY,
`grain` varchar(16) NOT NULL,
`period_start` datetime NOT NULL,
`period_end` datetime NOT NULL,
`dataset` varchar(64) NOT NULL DEFAULT 'all',
`tier` varchar(64) NOT NULL DEFAULT 'all',
`client` varchar(64) NOT NULL DEFAULT 'all',
`source` varchar(64) NOT NULL DEFAULT 'all',
`provider` varchar(128) NOT NULL,
`sessions` bigint NOT NULL DEFAULT 0,
`requests` bigint NOT NULL DEFAULT 0,
`input_tokens` bigint NOT NULL DEFAULT 0,
`output_tokens` bigint NOT NULL DEFAULT 0,
`reasoning_tokens` bigint NOT NULL DEFAULT 0,
`cache_read_tokens` bigint NOT NULL DEFAULT 0,
`total_tokens` bigint NOT NULL DEFAULT 0,
`input_cost_microcents` bigint NOT NULL DEFAULT 0,
`output_cost_microcents` bigint NOT NULL DEFAULT 0,
`total_cost_microcents` bigint NOT NULL DEFAULT 0,
`avg_duration_ms` decimal(12,2),
`p50_duration_ms` int,
`p95_duration_ms` int,
`avg_ttfb_ms` decimal(12,2),
`p50_ttfb_ms` int,
`p95_ttfb_ms` int,
`avg_output_tps` decimal(12,4),
`success_count` bigint NOT NULL DEFAULT 0,
`error_count` bigint NOT NULL DEFAULT 0,
`sample_count` bigint NOT NULL DEFAULT 0,
`market_share_tokens` decimal(10,6),
`market_share_requests` decimal(10,6),
`market_share_sessions` decimal(10,6),
`rank_by_tokens` int,
`rank_by_requests` int,
`rank_by_sessions` int,
`rank_by_cost` int,
`created_at` datetime NOT NULL DEFAULT (now()),
`updated_at` datetime NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `uniq_provider_period` UNIQUE INDEX(`grain`,`period_start`,`dataset`,`tier`,`client`,`source`,`provider`)
);
--> statement-breakpoint
RENAME TABLE `stat` TO `model_stat`;--> statement-breakpoint
CREATE INDEX `idx_country_map_tokens` ON `geo_stat` (`grain`,`period_start`,`dataset`,`tier`,`total_tokens`);--> statement-breakpoint
CREATE INDEX `idx_country_rank` ON `geo_stat` (`grain`,`period_start`,`dataset`,`tier`,`rank_by_tokens`);--> statement-breakpoint
CREATE INDEX `idx_country` ON `geo_stat` (`country`,`grain`,`period_start`);--> statement-breakpoint
CREATE INDEX `idx_continent` ON `geo_stat` (`continent`,`grain`,`period_start`);--> statement-breakpoint
CREATE INDEX `idx_provider_leaderboard_tokens` ON `provider_stat` (`grain`,`period_start`,`dataset`,`tier`,`total_tokens`);--> statement-breakpoint
CREATE INDEX `idx_provider_market_share` ON `provider_stat` (`grain`,`period_start`,`dataset`,`tier`,`market_share_tokens`);--> statement-breakpoint
CREATE INDEX `idx_provider_rank` ON `provider_stat` (`grain`,`period_start`,`dataset`,`tier`,`rank_by_tokens`);--> statement-breakpoint
CREATE INDEX `idx_provider` ON `provider_stat` (`provider`,`grain`,`period_start`);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
ALTER TABLE `geo_stat` ADD `provider` varchar(128) DEFAULT 'all' NOT NULL;--> statement-breakpoint
ALTER TABLE `geo_stat` ADD `model` varchar(256) DEFAULT 'all' NOT NULL;--> statement-breakpoint
ALTER TABLE `geo_stat` DROP INDEX `uniq_country_period`;--> statement-breakpoint
CREATE UNIQUE INDEX `uniq_country_period` ON `geo_stat` (`grain`,`period_start`,`dataset`,`tier`,`client`,`source`,`provider`,`model`,`country`);--> statement-breakpoint
CREATE INDEX `idx_country_model` ON `geo_stat` (`model`,`country`,`grain`,`period_start`);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
ALTER TABLE `geo_stat` ADD `period_key` varchar(32) NOT NULL;--> statement-breakpoint
ALTER TABLE `model_stat` ADD `period_key` varchar(32) NOT NULL;--> statement-breakpoint
ALTER TABLE `provider_stat` ADD `period_key` varchar(32) NOT NULL;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
ALTER TABLE `geo_stat` DROP COLUMN `period_start`;--> statement-breakpoint
ALTER TABLE `geo_stat` DROP COLUMN `period_end`;--> statement-breakpoint
ALTER TABLE `model_stat` DROP COLUMN `period_start`;--> statement-breakpoint
ALTER TABLE `model_stat` DROP COLUMN `period_end`;--> statement-breakpoint
ALTER TABLE `provider_stat` DROP COLUMN `period_start`;--> statement-breakpoint
ALTER TABLE `provider_stat` DROP COLUMN `period_end`;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-core",
"version": "1.17.4",
"private": true,
"type": "module",
"license": "MIT",
"exports": {
".": "./src/index.ts",
"./athena": "./src/athena.ts",
"./config": "./src/config.ts",
"./database": "./src/database.ts",
"./database/*": "./src/database/*.ts",
"./domain/*": "./src/domain/*.ts",
"./runtime": "./src/runtime.ts",
"./stat-sync": "./src/stat-sync.ts"
},
"scripts": {
"db:generate": "drizzle-kit generate --config=drizzle.config.ts",
"db:migrate": "bun src/migrate.ts",
"db:push": "drizzle-kit push --config=drizzle.config.ts",
"db:studio": "drizzle-kit studio --config=drizzle.config.ts",
"honeycomb:backfill": "bun src/honeycomb-backfill.ts",
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"@aws-sdk/client-athena": "3.933.0",
"@planetscale/database": "1.19.0",
"drizzle-orm": "catalog:",
"effect": "catalog:",
"sst": "catalog:"
},
"devDependencies": {
"@tsconfig/node22": "catalog:",
"@types/bun": "catalog:",
"@types/node": "catalog:",
"@typescript/native-preview": "catalog:",
"drizzle-kit": "catalog:",
"typescript": "catalog:"
},
"engines": {
"node": ">=22"
}
}

View File

@@ -0,0 +1,139 @@
import {
AthenaClient as AwsAthenaClient,
GetQueryExecutionCommand,
GetQueryResultsCommand,
StartQueryExecutionCommand,
type Row,
} from "@aws-sdk/client-athena"
import { Effect, Layer, Schema } from "effect"
import * as Context from "effect/Context"
import { Resource } from "sst/resource"
const ATHENA_MAX_POLL_ATTEMPTS = 60
const ATHENA_PAGE_SIZE = 1000
export type AthenaData = Record<string, string>
export class AthenaQueryError extends Schema.TaggedErrorClass<AthenaQueryError>()("AthenaQueryError", {
message: Schema.String,
queryExecutionId: Schema.optional(Schema.String),
cause: Schema.optional(Schema.Defect),
}) {}
export class AthenaQueryTimeoutError extends Schema.TaggedErrorClass<AthenaQueryTimeoutError>()(
"AthenaQueryTimeoutError",
{
message: Schema.String,
queryExecutionId: Schema.String,
},
) {}
export declare namespace Athena {
export interface Service {
readonly query: (query: string) => Effect.Effect<AthenaData[], AthenaQueryError | AthenaQueryTimeoutError>
}
}
export class Athena extends Context.Service<Athena, Athena.Service>()("@opencode/stats/Athena") {
static readonly layer: Layer.Layer<Athena> = Layer.effect(
Athena,
Effect.sync(() => {
const client = new AwsAthenaClient({ region: Resource.InferenceEvent.region })
const query = Effect.fn("Athena.query")(function* (query: string) {
const started = yield* Effect.tryPromise({
try: () =>
client.send(
new StartQueryExecutionCommand({
QueryString: query,
WorkGroup: Resource.InferenceEvent.workgroup,
QueryExecutionContext: {
Catalog: Resource.InferenceEvent.catalog,
Database: Resource.InferenceEvent.database,
},
}),
),
catch: (cause) => new AthenaQueryError({ message: "Failed to start Athena stats query", cause }),
})
const queryExecutionId = started.QueryExecutionId
if (!queryExecutionId)
return yield* new AthenaQueryError({ message: "Athena did not return a query execution id" })
yield* poll(client, queryExecutionId)
return yield* results(client, queryExecutionId)
})
return Athena.of({ query })
}),
)
}
const poll: (
client: AwsAthenaClient,
queryExecutionId: string,
attempt?: number,
) => Effect.Effect<void, AthenaQueryError | AthenaQueryTimeoutError> = Effect.fn("Athena.poll")(function* (
client: AwsAthenaClient,
queryExecutionId: string,
attempt = 0,
) {
if (attempt > 0) yield* Effect.sleep("2 seconds")
const result = yield* Effect.tryPromise({
try: () => client.send(new GetQueryExecutionCommand({ QueryExecutionId: queryExecutionId })),
catch: (cause) => new AthenaQueryError({ message: "Failed to poll Athena stats query", queryExecutionId, cause }),
})
const status = result.QueryExecution?.Status
if (status?.State === "SUCCEEDED") return
if (status?.State === "FAILED" || status?.State === "CANCELLED")
return yield* new AthenaQueryError({
message: `Athena stats query ${status.State.toLowerCase()}: ${status.StateChangeReason ?? "unknown reason"}`,
queryExecutionId,
})
if (attempt >= ATHENA_MAX_POLL_ATTEMPTS - 1)
return yield* new AthenaQueryTimeoutError({
message: `Athena stats query ${queryExecutionId} did not complete`,
queryExecutionId,
})
return yield* poll(client, queryExecutionId, attempt + 1)
})
const results: (
client: AwsAthenaClient,
queryExecutionId: string,
nextToken?: string,
) => Effect.Effect<AthenaData[], AthenaQueryError> = Effect.fn("Athena.results")(function* (
client: AwsAthenaClient,
queryExecutionId: string,
nextToken?: string,
) {
const result = yield* Effect.tryPromise({
try: () =>
client.send(
new GetQueryResultsCommand({
QueryExecutionId: queryExecutionId,
NextToken: nextToken,
MaxResults: ATHENA_PAGE_SIZE,
}),
),
catch: (cause) => new AthenaQueryError({ message: "Failed to read Athena stats results", queryExecutionId, cause }),
})
const columns = result.ResultSet?.ResultSetMetadata?.ColumnInfo?.map((item) => item.Name ?? "") ?? []
const rows = (result.ResultSet?.Rows ?? []).slice(nextToken ? 0 : 1).map((row) => rowData(columns, row))
if (!result.NextToken) return rows
return [...rows, ...(yield* results(client, queryExecutionId, result.NextToken))]
})
function rowData(columns: string[], row: Row): AthenaData {
return Object.fromEntries(
columns.flatMap((column, index) => {
const value = row.Data?.[index]?.VarCharValue
if (!column || value === undefined) return []
return [[column, value]]
}),
)
}

View File

@@ -0,0 +1,23 @@
import { Config, ConfigProvider, Effect, Layer, Schema } from "effect"
import * as Context from "effect/Context"
import { Resource } from "sst/resource"
export class AppConfigValue extends Schema.Class<AppConfigValue>("AppConfigValue")({
stage: Schema.NonEmptyString,
publicUrl: Schema.NonEmptyString,
}) {}
const decodeAppConfigValue = Schema.decodeUnknownSync(AppConfigValue)
const config = Config.all({
stage: Config.succeed(Resource.App.stage),
publicUrl: Config.string("PUBLIC_URL").pipe(Config.withDefault("http://localhost:3000")),
}).pipe(Config.map(decodeAppConfigValue))
export class AppConfig extends Context.Service<AppConfig, AppConfigValue>()("@opencode/stats/AppConfig") {
static readonly config = config
static readonly layer: Layer.Layer<AppConfig, never, never> = Layer.effect(
AppConfig,
config.parse(ConfigProvider.fromEnv()).pipe(Effect.orDie),
)
}

View File

@@ -0,0 +1,79 @@
import { Client } from "@planetscale/database"
import { drizzle } from "drizzle-orm/planetscale-serverless"
import { migrate as drizzleMigrate } from "drizzle-orm/planetscale-serverless/migrator"
import { Config, ConfigProvider, Effect, Layer, Schema } from "effect"
import * as Context from "effect/Context"
import * as schema from "./database/schema"
import { Resource } from "sst/resource"
export const DatabaseUrl = Schema.NonEmptyString.pipe(Schema.brand("DatabaseUrl"))
export type DatabaseUrl = typeof DatabaseUrl.Type
export class DatabaseSettings extends Schema.Class<DatabaseSettings>("DatabaseSettings")({
url: DatabaseUrl,
migrationsDir: Schema.NonEmptyString,
}) {}
const decodeDatabaseSettings = Schema.decodeUnknownSync(DatabaseSettings)
const config = Config.all({
url: Config.nonEmptyString("DATABASE_URL").pipe(Config.withDefault(Resource.StatsDatabase.url)),
migrationsDir: Config.nonEmptyString("DATABASE_MIGRATIONS_DIR").pipe(Config.withDefault("./migrations")),
}).pipe(Config.map(decodeDatabaseSettings))
export class DatabaseConfig extends Context.Service<DatabaseConfig, DatabaseSettings>()(
"@opencode/stats/DatabaseConfig",
) {
static readonly config = config
static readonly layer: Layer.Layer<DatabaseConfig, never, never> = Layer.effect(
DatabaseConfig,
config.parse(ConfigProvider.fromEnv()).pipe(Effect.orDie),
)
}
function makeDrizzle(settings: DatabaseSettings) {
return drizzle({ client: new Client({ url: settings.url }), schema })
}
export type Drizzle = ReturnType<typeof makeDrizzle>
export class DrizzleClient extends Context.Service<DrizzleClient, Drizzle>()("@opencode/stats/DrizzleClient") {
static readonly layer: Layer.Layer<DrizzleClient, never, DatabaseConfig> = Layer.effect(
DrizzleClient,
Effect.map(DatabaseConfig, makeDrizzle),
)
}
export class DatabaseError extends Schema.TaggedErrorClass<DatabaseError>()("DatabaseError", {
cause: Schema.Defect,
}) {}
export const catchDbError = Effect.mapError((cause) => DatabaseError.make({ cause }))
export class MigrationError extends Schema.TaggedErrorClass<MigrationError>()("MigrationError", {
message: Schema.String,
cause: Schema.optional(Schema.Defect),
}) {}
export const migrate = Effect.fn("Database.migrate")(function* () {
const settings = yield* DatabaseConfig
yield* Effect.logInfo("applying database migrations").pipe(
Effect.annotateLogs({ migrationsDir: settings.migrationsDir }),
)
const result = yield* Effect.tryPromise({
try: () =>
drizzleMigrate(drizzle({ client: new Client({ url: settings.url }) }), {
migrationsFolder: settings.migrationsDir,
}),
catch: (cause) => new MigrationError({ message: "Failed to apply database migrations", cause }),
})
if (result)
return yield* new MigrationError({
message: `Failed to initialize database migrations: ${result.exitCode}`,
})
yield* Effect.logInfo("database migrations complete").pipe(
Effect.annotateLogs({ migrationsDir: settings.migrationsDir }),
)
})
export const layer = Layer.mergeAll(DatabaseConfig.layer, DrizzleClient.layer.pipe(Layer.provide(DatabaseConfig.layer)))

View File

@@ -0,0 +1,160 @@
import { bigint, char, datetime, decimal, index, int, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
export const modelStat = mysqlTable(
"model_stat",
{
...periodColumns(),
provider: varchar({ length: 128 }).notNull(),
model: varchar({ length: 256 }).notNull(),
provider_model: varchar({ length: 256 }).notNull().default(""),
...metricColumns(),
rank_by_tokens: int(),
rank_by_requests: int(),
rank_by_cost: int(),
...timestampColumns(),
},
(table) => [
uniqueIndex("uniq_model_period").on(
table.grain,
table.period_key,
table.dataset,
table.tier,
table.client,
table.source,
table.provider,
table.model,
),
index("idx_leaderboard_tokens").on(table.grain, table.period_key, table.dataset, table.tier, table.total_tokens),
index("idx_model").on(table.model, table.grain, table.period_key),
],
)
export const providerStat = mysqlTable(
"provider_stat",
{
...periodColumns(),
provider: varchar({ length: 128 }).notNull(),
...metricColumns(),
...marketShareColumns(),
rank_by_tokens: int(),
rank_by_requests: int(),
rank_by_sessions: int(),
rank_by_cost: int(),
...timestampColumns(),
},
(table) => [
uniqueIndex("uniq_provider_period").on(
table.grain,
table.period_key,
table.dataset,
table.tier,
table.client,
table.source,
table.provider,
),
index("idx_provider_leaderboard_tokens").on(
table.grain,
table.period_key,
table.dataset,
table.tier,
table.total_tokens,
),
index("idx_provider_market_share").on(
table.grain,
table.period_key,
table.dataset,
table.tier,
table.market_share_tokens,
),
index("idx_provider_rank").on(table.grain, table.period_key, table.dataset, table.tier, table.rank_by_tokens),
index("idx_provider").on(table.provider, table.grain, table.period_key),
],
)
export const geoStat = mysqlTable(
"geo_stat",
{
...periodColumns(),
provider: varchar({ length: 128 }).notNull().default("all"),
model: varchar({ length: 256 }).notNull().default("all"),
country: char({ length: 2 }).notNull(),
continent: varchar({ length: 8 }).notNull().default(""),
...metricColumns(),
...marketShareColumns(),
rank_by_tokens: int(),
rank_by_requests: int(),
rank_by_sessions: int(),
rank_by_cost: int(),
...timestampColumns(),
},
(table) => [
uniqueIndex("uniq_country_period").on(
table.grain,
table.period_key,
table.dataset,
table.tier,
table.client,
table.source,
table.provider,
table.model,
table.country,
),
index("idx_country_map_tokens").on(table.grain, table.period_key, table.dataset, table.tier, table.total_tokens),
index("idx_country_rank").on(table.grain, table.period_key, table.dataset, table.tier, table.rank_by_tokens),
index("idx_country").on(table.country, table.grain, table.period_key),
index("idx_continent").on(table.continent, table.grain, table.period_key),
index("idx_country_model").on(table.model, table.country, table.grain, table.period_key),
],
)
function periodColumns() {
return {
id: bigint({ mode: "number" }).autoincrement().primaryKey(),
grain: varchar({ length: 16 }).notNull(),
period_key: varchar({ length: 32 }).notNull(),
dataset: varchar({ length: 64 }).notNull().default("all"),
tier: varchar({ length: 64 }).notNull().default("all"),
client: varchar({ length: 64 }).notNull().default("all"),
source: varchar({ length: 64 }).notNull().default("all"),
}
}
function metricColumns() {
return {
sessions: bigint({ mode: "number" }).notNull().default(0),
requests: bigint({ mode: "number" }).notNull().default(0),
input_tokens: bigint({ mode: "number" }).notNull().default(0),
output_tokens: bigint({ mode: "number" }).notNull().default(0),
reasoning_tokens: bigint({ mode: "number" }).notNull().default(0),
cache_read_tokens: bigint({ mode: "number" }).notNull().default(0),
total_tokens: bigint({ mode: "number" }).notNull().default(0),
input_cost_microcents: bigint({ mode: "number" }).notNull().default(0),
output_cost_microcents: bigint({ mode: "number" }).notNull().default(0),
total_cost_microcents: bigint({ mode: "number" }).notNull().default(0),
avg_duration_ms: decimal({ precision: 12, scale: 2, mode: "number" }),
p50_duration_ms: int(),
p95_duration_ms: int(),
avg_ttfb_ms: decimal({ precision: 12, scale: 2, mode: "number" }),
p50_ttfb_ms: int(),
p95_ttfb_ms: int(),
avg_output_tps: decimal({ precision: 12, scale: 4, mode: "number" }),
success_count: bigint({ mode: "number" }).notNull().default(0),
error_count: bigint({ mode: "number" }).notNull().default(0),
sample_count: bigint({ mode: "number" }).notNull().default(0),
}
}
function marketShareColumns() {
return {
market_share_tokens: decimal({ precision: 10, scale: 6, mode: "number" }),
market_share_requests: decimal({ precision: 10, scale: 6, mode: "number" }),
market_share_sessions: decimal({ precision: 10, scale: 6, mode: "number" }),
}
}
function timestampColumns() {
return {
created_at: datetime({ mode: "date" }).notNull().defaultNow(),
updated_at: datetime({ mode: "date" }).notNull().defaultNow().onUpdateNow(),
}
}

View File

@@ -0,0 +1,232 @@
import { and, asc, eq, inArray, or } from "drizzle-orm"
import { Effect, Layer } from "effect"
import * as Context from "effect/Context"
import { DatabaseError, DrizzleClient } from "../database"
import { geoStat } from "../database/schema"
import { RETIRED_STAT_MODELS, RETIRED_STAT_PROVIDERS } from "./model-normalization"
import {
chunks,
collapseRows,
inserted,
rankRowsWithMarketShare,
statPeriodKey,
statRowScope,
synthesizeAllTierRows,
toStatBaseRow,
UPSERT_CHUNK_SIZE,
type StatBaseAggregate,
} from "./stat"
export type GeoStatRow = typeof geoStat.$inferInsert
export type GeoStatAggregate = StatBaseAggregate & {
provider: string
model: string
country: string
continent: string
}
export type GeoStatMetric = {
periodKey: string
updatedAt: Date
tier: string
provider: string
model: string
country: string
continent: string
totalTokens: number
}
export declare namespace GeoStatRepo {
export interface Service {
readonly listDaily: (opts?: {
readonly provider?: string
readonly model?: string
}) => Effect.Effect<GeoStatMetric[], DatabaseError>
readonly listByPeriod: (opts: {
readonly grain: string
readonly periodKey: string
readonly dataset?: string
readonly tier?: string
readonly client?: string
readonly source?: string
readonly provider?: string
readonly model?: string
}) => Effect.Effect<GeoStatRow[], DatabaseError>
readonly upsert: (rows: GeoStatRow[]) => Effect.Effect<void, DatabaseError>
readonly deleteRetiredDimensions: (rows: GeoStatRow[]) => Effect.Effect<void, DatabaseError>
}
}
export class GeoStatRepo extends Context.Service<GeoStatRepo, GeoStatRepo.Service>()("@opencode/stats/GeoStatRepo") {
static readonly layer: Layer.Layer<GeoStatRepo, never, DrizzleClient> = Layer.effect(
GeoStatRepo,
Effect.gen(function* () {
const db = yield* DrizzleClient
const listDaily = Effect.fn("GeoStatRepo.listDaily")(function* (opts?: {
readonly provider?: string
readonly model?: string
}) {
const scope =
opts?.model && opts.provider
? and(eq(geoStat.provider, opts.provider), eq(geoStat.model, opts.model))
: opts?.model
? eq(geoStat.model, opts.model)
: and(eq(geoStat.provider, "all"), eq(geoStat.model, "all"))
return yield* Effect.tryPromise({
try: () =>
db
.select({
periodKey: geoStat.period_key,
updatedAt: geoStat.updated_at,
tier: geoStat.tier,
provider: geoStat.provider,
model: geoStat.model,
country: geoStat.country,
continent: geoStat.continent,
totalTokens: geoStat.total_tokens,
})
.from(geoStat)
.where(and(eq(geoStat.grain, "day"), eq(geoStat.client, "all"), eq(geoStat.source, "all"), scope))
.orderBy(asc(geoStat.period_key)),
catch: (cause) => DatabaseError.make({ cause }),
})
})
const listByPeriod = Effect.fn("GeoStatRepo.listByPeriod")(function* (opts: {
readonly grain: string
readonly periodKey: string
readonly dataset?: string
readonly tier?: string
readonly client?: string
readonly source?: string
readonly provider?: string
readonly model?: string
}) {
return yield* Effect.tryPromise({
try: () =>
db
.select()
.from(geoStat)
.where(
and(
eq(geoStat.grain, opts.grain),
eq(geoStat.period_key, opts.periodKey),
eq(geoStat.dataset, opts.dataset ?? "zen"),
eq(geoStat.tier, opts.tier ?? "all"),
eq(geoStat.client, opts.client ?? "all"),
eq(geoStat.source, opts.source ?? "all"),
eq(geoStat.provider, opts.provider ?? "all"),
eq(geoStat.model, opts.model ?? "all"),
),
),
catch: (cause) => DatabaseError.make({ cause }),
})
})
const upsert = Effect.fn("GeoStatRepo.upsert")(function* (rows: GeoStatRow[]) {
yield* Effect.forEach(
chunks(rows, UPSERT_CHUNK_SIZE),
(chunk) =>
Effect.tryPromise({
try: () =>
db
.insert(geoStat)
.values(chunk)
.onDuplicateKeyUpdate({
set: {
continent: inserted("continent"),
sessions: inserted("sessions"),
requests: inserted("requests"),
input_tokens: inserted("input_tokens"),
output_tokens: inserted("output_tokens"),
reasoning_tokens: inserted("reasoning_tokens"),
cache_read_tokens: inserted("cache_read_tokens"),
total_tokens: inserted("total_tokens"),
input_cost_microcents: inserted("input_cost_microcents"),
output_cost_microcents: inserted("output_cost_microcents"),
total_cost_microcents: inserted("total_cost_microcents"),
avg_duration_ms: inserted("avg_duration_ms"),
p50_duration_ms: inserted("p50_duration_ms"),
p95_duration_ms: inserted("p95_duration_ms"),
avg_ttfb_ms: inserted("avg_ttfb_ms"),
p50_ttfb_ms: inserted("p50_ttfb_ms"),
p95_ttfb_ms: inserted("p95_ttfb_ms"),
avg_output_tps: inserted("avg_output_tps"),
success_count: inserted("success_count"),
error_count: inserted("error_count"),
sample_count: inserted("sample_count"),
market_share_tokens: inserted("market_share_tokens"),
market_share_requests: inserted("market_share_requests"),
market_share_sessions: inserted("market_share_sessions"),
rank_by_tokens: inserted("rank_by_tokens"),
rank_by_requests: inserted("rank_by_requests"),
rank_by_sessions: inserted("rank_by_sessions"),
rank_by_cost: inserted("rank_by_cost"),
},
}),
catch: (cause) => DatabaseError.make({ cause }),
}),
{ discard: true },
)
})
const deleteRetiredDimensions = Effect.fn("GeoStatRepo.deleteRetiredDimensions")(function* (rows: GeoStatRow[]) {
const scope = statRowScope(rows)
if (!scope) return
yield* Effect.tryPromise({
try: () =>
db
.delete(geoStat)
.where(
and(
inArray(geoStat.grain, scope.grains),
inArray(geoStat.period_key, scope.periodKeys),
inArray(geoStat.dataset, scope.datasets),
inArray(geoStat.client, scope.clients),
inArray(geoStat.source, scope.sources),
or(inArray(geoStat.provider, RETIRED_STAT_PROVIDERS), inArray(geoStat.model, RETIRED_STAT_MODELS)),
),
),
catch: (cause) => DatabaseError.make({ cause }),
})
})
return GeoStatRepo.of({ listDaily, listByPeriod, upsert, deleteRetiredDimensions })
}),
)
}
export function rowsFromAggregates(aggregates: GeoStatAggregate[]) {
return rankRowsWithMarketShare(
[
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "week").map(toRow), dimensionKey),
dimensionKey,
),
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "day").map(toRow), dimensionKey),
dimensionKey,
),
],
marketShareKey,
)
}
function toRow(data: GeoStatAggregate): GeoStatRow {
return {
...toStatBaseRow(data),
provider: data.provider,
model: data.model,
country: data.country,
continent: data.continent,
}
}
function dimensionKey(row: GeoStatRow) {
return [row.provider, row.model, row.country].join("\u0000")
}
function marketShareKey(row: GeoStatRow) {
return [statPeriodKey(row), row.provider, row.model].join("\u0000")
}

View File

@@ -0,0 +1,876 @@
import { Effect } from "effect"
import { DatabaseError } from "../database"
import { GeoStatRepo, type GeoStatMetric } from "./geo"
import { ModelStatRepo, type ModelStatMetric } from "./model"
import { ProviderStatRepo, type ProviderStatMetric } from "./provider"
export type UsageProduct = "All Users" | "Zen" | "Go" | "Enterprise"
export type TokenProduct = "Zen" | "Go" | "Enterprise"
export type UsageRange = "1D" | "1W" | "2W" | "1M" | "2M" | "3M" | "YTD" | "ALL"
export type UsagePoint = { date: string; segments: { model: string; value: number }[] }
export type MarketDay = { date: string; total: number; authors: { author: string; share: number; tokens: number }[] }
export type LeaderboardEntry = {
model: string
provider: string
author: string
tokens: number
change: number | null
rank: number
}
export type TokenCostEntry = { model: string; total: number; input: number; output: number; cached: number }
export type CacheRatioEntry = { model: string; ratio: number; cached: number; uncached: number; total: number }
export type SessionCostEntry = { model: string; cost: number; tokens: number }
export type CountryEntry = { country: string; continent: string; tokens: number; share: number; rank: number }
export type ModelUsagePoint = { date: string; tokens: number; sessions: number; cost: number }
export type ModelMixEntry = { label: string; tokens: number; share: number }
export type ModelProductEntry = { product: string; tokens: number; sessions: number; share: number }
export type ModelPeerEntry = {
model: string
provider: string
author: string
rank: number
tokens: number
share: number
slug: string
}
export type LabUsageModelEntry = {
model: string
provider: string
author: string
tokens: number
share: number
slug: string
}
export type StatsModelData = {
updatedAt: string | null
model: string
slug: string
provider: string
author: string
rank: number
previousRank: number | null
totalModels: number
tokenShare: number
tokenChange: number
totals: {
sessions: number
tokens: number
cost: number
tokensPerSession: number
costPerSession: number
costPerMillion: number
cacheRatio: number
}
usage: ModelUsagePoint[]
tokenMix: ModelMixEntry[]
productMix: ModelProductEntry[]
country: Record<UsageRange, CountryEntry[]>
peers: ModelPeerEntry[]
}
export type StatsLabData = {
updatedAt: string | null
provider: string
author: string
tokenShare: number
tokenChange: number
totals: {
sessions: number
tokens: number
models: number
}
usage: ModelUsagePoint[]
models: LabUsageModelEntry[]
}
export type StatsHomeData = {
updatedAt: string | null
usage: Record<UsageProduct, Record<UsageRange, UsagePoint[]>>
leaderboard: Record<UsageProduct, Record<UsageRange, LeaderboardEntry[]>>
market: Record<UsageRange, MarketDay[]>
tokenCost: Record<TokenProduct, TokenCostEntry[]>
cacheRatio: Record<TokenProduct, CacheRatioEntry[]>
sessionCost: Record<TokenProduct, SessionCostEntry[]>
country: Record<UsageRange, CountryEntry[]>
}
const DAY_MS = 86_400_000
const TOKEN_SCALE = 1_000_000
const DOLLARS_PER_MICROCENT = 1 / 100_000_000
const METRIC_MODEL_LIMIT = 10
const LEADERBOARD_CHANGE_MIN_MULTIPLE = 10
const months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"] as const
type StatMetricRow = Omit<ModelStatMetric, "updatedAt"> & {
periodStart: number
updatedAt: number
}
type ProviderMetricRow = Omit<ProviderStatMetric, "updatedAt"> & {
periodStart: number
updatedAt: number
}
type GeoMetricRow = Omit<GeoStatMetric, "updatedAt"> & {
periodStart: number
updatedAt: number
}
type DateWindow = { start: number; end: number; previousStart: number; previousEnd: number }
type Bucket = { start: number; end: number; label: string }
type ModelAggregate = {
model: string
provider: string
sessions: number
inputTokens: number
outputTokens: number
reasoningTokens: number
cacheReadTokens: number
totalTokens: number
inputCostMicrocents: number
outputCostMicrocents: number
totalCostMicrocents: number
}
export const getStatsHomeData: () => Effect.Effect<
StatsHomeData,
DatabaseError,
ModelStatRepo | ProviderStatRepo | GeoStatRepo
> = Effect.fn("StatsHome.getData")(function* () {
const modelStats = yield* ModelStatRepo
const providerStats = yield* ProviderStatRepo
const geoStats = yield* GeoStatRepo
const [modelRows, providerRows, geoRows] = yield* Effect.all(
[modelStats.listDaily(), providerStats.listDaily(), geoStats.listDaily()],
{ concurrency: "unbounded" },
)
return buildStatsHomeData(modelRows, providerRows, geoRows)
})
export const getStatsModelData: (
model: string,
provider?: string,
) => Effect.Effect<StatsModelData | null, DatabaseError, ModelStatRepo | GeoStatRepo> = Effect.fn("StatsModel.getData")(
function* (model, provider) {
const modelStats = yield* ModelStatRepo
const geoStats = yield* GeoStatRepo
const modelRows = yield* modelStats.listDaily()
const normalized = modelRows.flatMap(normalizeStatRow)
const resolvedModel = resolveModelName(model, normalized, provider)
if (!resolvedModel) return null
return buildStatsModelData(
resolvedModel,
modelRows,
yield* geoStats.listDaily({
model: resolvedModel,
provider: resolveModelProvider(resolvedModel, normalized, provider),
}),
provider,
)
},
)
export const getStatsLabData: (provider: string) => Effect.Effect<StatsLabData | null, DatabaseError, ModelStatRepo> =
Effect.fn("StatsLab.getData")(function* (provider) {
const modelStats = yield* ModelStatRepo
return buildStatsLabData(provider, yield* modelStats.listDaily())
})
function buildStatsHomeData(
modelRows: ModelStatMetric[],
providerRows: ProviderStatMetric[],
geoRows: GeoStatMetric[],
): StatsHomeData {
const normalized = modelRows.flatMap(normalizeStatRow)
const providers = providerRows.flatMap(normalizeProviderRow)
const geo = geoRows.flatMap(normalizeGeoRow)
const periods = [...normalized, ...providers, ...geo]
if (periods.length === 0) return emptyStatsHomeData()
const earliest = Math.min(...periods.map((row) => row.periodStart))
const latest = Math.max(...periods.map((row) => row.periodStart))
const latestUpdate = Math.max(...periods.map((row) => row.updatedAt))
return {
updatedAt: new Date(latestUpdate).toISOString(),
usage: createUsageProductRecord((product) =>
createRangeRecord((range) => buildUsagePoints(normalized, product, range, getWindow(range, earliest, latest))),
),
leaderboard: createUsageProductRecord((product) =>
createRangeRecord((range) => buildLeaderboard(normalized, product, getWindow(range, earliest, latest))),
),
market: createRangeRecord((range) => buildMarketShare(providers, "Go", range, getWindow(range, earliest, latest))),
tokenCost: createTokenProductRecord((product) =>
buildTokenCost(normalized, product, getWindow("1W", earliest, latest)),
),
cacheRatio: createTokenProductRecord((product) =>
buildCacheRatio(normalized, product, getWindow("1W", earliest, latest)),
),
sessionCost: createTokenProductRecord((product) =>
buildSessionCost(normalized, product, getWindow("1W", earliest, latest)),
),
country: createRangeRecord((range) => buildCountryStats(geo, getWindow(range, earliest, latest))),
}
}
function buildStatsModelData(
modelParam: string,
modelRows: ModelStatMetric[],
geoRows: GeoStatMetric[],
providerParam?: string,
): StatsModelData | null {
const normalized = modelRows.flatMap(normalizeStatRow)
const geo = geoRows.flatMap(normalizeGeoRow)
if (normalized.length === 0) return null
const model = resolveModelName(modelParam, normalized, providerParam)
if (!model) return null
const modelScopedRows = normalized.filter((row) => row.model === model)
const earliest = Math.min(...normalized.map((row) => row.periodStart))
const latest = Math.max(...normalized.map((row) => row.periodStart))
const latestUpdate = Math.max(...modelScopedRows.map((row) => row.updatedAt))
const window = getWindow("2M", earliest, latest)
const currentRows = rowsForProduct(modelScopedRows, "All Users", window.start, window.end)
const previousRows = rowsForProduct(modelScopedRows, "All Users", window.previousStart, window.previousEnd)
const current = combineRowsForModel(model, currentRows)
const previous = combineRowsForModel(model, previousRows)
const peers = aggregateByModelName(rowsForProduct(normalized, "All Users", window.start, window.end))
.filter((item) => item.totalTokens > 0)
.toSorted((a, b) => b.totalTokens - a.totalTokens || a.model.localeCompare(b.model))
const previousPeers = aggregateByModelName(
rowsForProduct(normalized, "All Users", window.previousStart, window.previousEnd),
)
.filter((item) => item.totalTokens > 0)
.toSorted((a, b) => b.totalTokens - a.totalTokens || a.model.localeCompare(b.model))
const rank = Math.max(1, peers.findIndex((item) => item.model === model) + 1)
const previousRankIndex = previousPeers.findIndex((item) => item.model === model)
const totalTokens = peers.reduce((sum, item) => sum + item.totalTokens, 0)
return {
updatedAt: Number.isFinite(latestUpdate) ? new Date(latestUpdate).toISOString() : null,
model,
slug: modelSlug(model),
provider: current.provider,
author: formatProvider(current.provider),
rank,
previousRank: previousRankIndex >= 0 ? previousRankIndex + 1 : null,
totalModels: peers.length,
tokenShare: totalTokens > 0 ? round((current.totalTokens / totalTokens) * 100, 2) : 0,
tokenChange: percentChange(current.totalTokens, previous.totalTokens),
totals: {
sessions: current.sessions,
tokens: current.totalTokens,
cost: round(microcentsToDollars(current.totalCostMicrocents), 2),
tokensPerSession: current.sessions > 0 ? Math.round(current.totalTokens / current.sessions) : 0,
costPerSession:
current.sessions > 0 ? round(microcentsToDollars(current.totalCostMicrocents) / current.sessions, 4) : 0,
costPerMillion: costPerMillion(current.totalCostMicrocents, current.totalTokens),
cacheRatio:
current.inputTokens + current.cacheReadTokens > 0
? round((current.cacheReadTokens / (current.inputTokens + current.cacheReadTokens)) * 100, 1)
: 0,
},
usage: buildModelUsage(currentRows, window, "2M"),
tokenMix: buildModelTokenMix(current),
productMix: buildModelProductMix(modelScopedRows, window, current),
country: createRangeRecord((range) => buildCountryStats(geo, getWindow(range, earliest, latest))),
peers: buildModelPeers(peers, rank, totalTokens),
}
}
function buildStatsLabData(providerParam: string, modelRows: ModelStatMetric[]): StatsLabData | null {
const normalized = modelRows.flatMap(normalizeStatRow)
if (normalized.length === 0) return null
const provider = resolveProviderName(providerParam, normalized)
if (!provider) return null
const providerRows = normalized.filter((row) => providerMatches(row.provider, provider))
if (providerRows.length === 0) return null
const earliest = Math.min(...normalized.map((row) => row.periodStart))
const latest = Math.max(...normalized.map((row) => row.periodStart))
const latestUpdate = Math.max(...providerRows.map((row) => row.updatedAt))
const window = getWindow("2M", earliest, latest)
const currentRows = rowsForProduct(providerRows, "All Users", window.start, window.end)
const previousRows = rowsForProduct(providerRows, "All Users", window.previousStart, window.previousEnd)
const current = combineRowsForModel("", currentRows)
const previous = combineRowsForModel("", previousRows)
const allCurrent = aggregateByModel(rowsForProduct(normalized, "All Users", window.start, window.end))
const totalTokens = allCurrent.reduce((sum, item) => sum + item.totalTokens, 0)
const models = aggregateByModel(currentRows)
.filter((item) => item.totalTokens > 0)
.toSorted((a, b) => b.totalTokens - a.totalTokens || a.model.localeCompare(b.model))
return {
updatedAt: Number.isFinite(latestUpdate) ? new Date(latestUpdate).toISOString() : null,
provider,
author: formatProvider(provider),
tokenShare: totalTokens > 0 ? round((current.totalTokens / totalTokens) * 100, 2) : 0,
tokenChange: percentChange(current.totalTokens, previous.totalTokens),
totals: {
sessions: current.sessions,
tokens: current.totalTokens,
models: models.length,
},
usage: buildModelUsage(currentRows, window, "2M"),
models: models.map((item) => ({
model: item.model,
provider: item.provider,
author: formatProvider(item.provider),
tokens: item.totalTokens,
share: current.totalTokens > 0 ? round((item.totalTokens / current.totalTokens) * 100, 2) : 0,
slug: modelSlug(item.model),
})),
}
}
function emptyStatsHomeData(): StatsHomeData {
return {
updatedAt: null,
usage: createUsageProductRecord(() => createRangeRecord(() => [])),
leaderboard: createUsageProductRecord(() => createRangeRecord(() => [])),
market: createRangeRecord(() => []),
tokenCost: createTokenProductRecord(() => []),
cacheRatio: createTokenProductRecord(() => []),
sessionCost: createTokenProductRecord(() => []),
country: createRangeRecord(() => []),
}
}
function buildUsagePoints(rows: StatMetricRow[], product: UsageProduct, range: UsageRange, window: DateWindow) {
const windowRows = rowsForProduct(rows, product, window.start, window.end)
const modelOrder = aggregateByModel(windowRows)
.toSorted((a, b) => b.totalTokens - a.totalTokens)
.slice(0, 6)
.map((item) => ({ key: modelKey(item.provider, item.model), model: item.model }))
return createBuckets(window, range).map((bucket) => {
const bucketRows = aggregateByModel(rowsForProduct(rows, product, bucket.start, bucket.end))
const byModel = new Map(bucketRows.map((item) => [modelKey(item.provider, item.model), item.totalTokens]))
const segmentTokens = modelOrder.map((model) => ({ model: model.model, tokens: byModel.get(model.key) ?? 0 }))
const knownTokens = segmentTokens.reduce((sum, item) => sum + item.tokens, 0)
const totalTokens = bucketRows.reduce((sum, item) => sum + item.totalTokens, 0)
return {
date: bucket.label,
segments: [
...segmentTokens.map((item) => ({ model: item.model, value: round(item.tokens / 1_000_000_000_000, 4) })),
{ model: "Other", value: round(Math.max(totalTokens - knownTokens, 0) / 1_000_000_000_000, 4) },
],
}
})
}
function buildLeaderboard(rows: StatMetricRow[], product: UsageProduct, window: DateWindow) {
const previous = new Map(
aggregateByModel(rowsForProduct(rows, product, window.previousStart, window.previousEnd)).map((item) => [
modelKey(item.provider, item.model),
item.totalTokens,
]),
)
return aggregateByModel(rowsForProduct(rows, product, window.start, window.end))
.toSorted((a, b) => b.totalTokens - a.totalTokens)
.slice(0, 18)
.map((item, index) => ({
model: item.model,
provider: item.provider,
author: formatProvider(item.provider),
tokens: Math.round(item.totalTokens / 1_000_000_000),
change: leaderboardChange(item.totalTokens, previous.get(modelKey(item.provider, item.model)) ?? 0),
rank: index + 1,
}))
}
function buildMarketShare(rows: ProviderMetricRow[], product: UsageProduct, range: UsageRange, window: DateWindow) {
return createBuckets(window, range).flatMap((bucket) => {
const total = aggregateByProvider(rowsForProduct(rows, product, bucket.start, bucket.end)).toSorted(
(a, b) => b.tokens - a.tokens,
)
const totalTokens = total.reduce((sum, item) => sum + item.tokens, 0)
if (totalTokens === 0) return []
const authors = total.slice(0, 8)
const knownTokens = authors.reduce((sum, item) => sum + item.tokens, 0)
const withOther = [...authors, { provider: "Other", tokens: Math.max(totalTokens - knownTokens, 0) }].filter(
(item) => item.tokens > 0,
)
return [
{
date: bucket.label,
total: round(totalTokens / 1_000_000_000_000, 2),
authors: withOther.map((item) => ({
author: item.provider === "Other" ? "Other" : formatProvider(item.provider),
share: round((item.tokens / totalTokens) * 100, 1),
tokens: round(item.tokens / 1_000_000_000_000, 2),
})),
},
]
})
}
function buildCountryStats(rows: GeoMetricRow[], window: DateWindow) {
const countries = aggregateByCountry(rowsForProduct(rows, "All Users", window.start, window.end))
.filter((item) => item.tokens > 0 && item.country !== "AQ")
.toSorted((a, b) => b.tokens - a.tokens)
const totalTokens = countries.reduce((sum, item) => sum + item.tokens, 0)
if (totalTokens === 0) return []
return countries.map((item, index) => ({
country: item.country,
continent: item.continent,
tokens: round(item.tokens / 1_000_000_000_000, 4),
share: round((item.tokens / totalTokens) * 100, 1),
rank: index + 1,
}))
}
function buildTokenCost(rows: StatMetricRow[], product: TokenProduct, window: DateWindow) {
return topModelsByUsage(rows, product, window)
.flatMap((item) => {
const total = costPerMillion(item.totalCostMicrocents, item.totalTokens)
if (total === 0) return []
return [
{
model: item.model,
total,
input: costPerMillion(item.inputCostMicrocents, item.inputTokens),
output: costPerMillion(item.outputCostMicrocents, item.outputTokens + item.reasoningTokens),
cached: costPerMillion(item.inputCostMicrocents, item.inputTokens + item.cacheReadTokens),
},
]
})
.toSorted((a, b) => a.total - b.total)
}
function buildCacheRatio(rows: StatMetricRow[], product: TokenProduct, window: DateWindow) {
return topModelsByUsage(rows, product, window)
.flatMap((item) => {
const total = item.inputTokens + item.cacheReadTokens
if (total === 0) return []
return [
{
model: item.model,
ratio: round((item.cacheReadTokens / total) * 100, 1),
cached: round(item.cacheReadTokens / 1_000_000_000, 1),
uncached: round(item.inputTokens / 1_000_000_000, 1),
total: round(total / 1_000_000_000, 1),
},
]
})
.toSorted((a, b) => b.ratio - a.ratio || b.cached - a.cached)
}
function buildSessionCost(rows: StatMetricRow[], product: TokenProduct, window: DateWindow) {
return topModelsByUsage(rows, product, window)
.flatMap((item) => {
if (item.sessions === 0) return []
const cost = round(microcentsToDollars(item.totalCostMicrocents) / item.sessions, 4)
if (cost === 0) return []
return [{ model: item.model, cost, tokens: Math.round(item.totalTokens / item.sessions) }]
})
.toSorted((a, b) => a.cost - b.cost)
}
function topModelsByUsage(rows: StatMetricRow[], product: TokenProduct, window: DateWindow) {
return aggregateByModel(rowsForProduct(rows, product, window.start, window.end))
.toSorted((a, b) => b.totalTokens - a.totalTokens)
.slice(0, METRIC_MODEL_LIMIT)
}
function buildModelUsage(rows: StatMetricRow[], window: DateWindow, range: UsageRange) {
return createBuckets(window, range).map((bucket) => {
const aggregate = combineRowsForModel(
"",
rows.filter((row) => row.periodStart >= bucket.start && row.periodStart < bucket.end),
)
return {
date: bucket.label,
tokens: aggregate.totalTokens,
sessions: aggregate.sessions,
cost: round(microcentsToDollars(aggregate.totalCostMicrocents), 2),
}
})
}
function buildModelTokenMix(aggregate: ModelAggregate): ModelMixEntry[] {
const items = [
{ label: "Input", tokens: aggregate.inputTokens },
{ label: "Output", tokens: aggregate.outputTokens },
{ label: "Reasoning", tokens: aggregate.reasoningTokens },
{ label: "Cached", tokens: aggregate.cacheReadTokens },
].filter((item) => item.tokens > 0)
const total = items.reduce((sum, item) => sum + item.tokens, 0)
if (total === 0) return []
return items.map((item) => ({ ...item, share: round((item.tokens / total) * 100, 1) }))
}
function buildModelProductMix(
rows: StatMetricRow[],
window: DateWindow,
fallback: ModelAggregate,
): ModelProductEntry[] {
const products = ["Go", "Zen", "Enterprise"] as const
const items = products.flatMap((product) => {
const aggregate = combineRowsForModel(
fallback.model,
rows.filter((row) => row.tier === product && row.periodStart >= window.start && row.periodStart < window.end),
)
if (aggregate.totalTokens === 0) return []
return [{ product, tokens: aggregate.totalTokens, sessions: aggregate.sessions }]
})
const total = items.reduce((sum, item) => sum + item.tokens, 0)
if (total > 0) return items.map((item) => ({ ...item, share: round((item.tokens / total) * 100, 1) }))
if (fallback.totalTokens === 0) return []
return [{ product: "All Users", tokens: fallback.totalTokens, sessions: fallback.sessions, share: 100 }]
}
function buildModelPeers(peers: ModelAggregate[], rank: number, totalTokens: number): ModelPeerEntry[] {
const start = Math.max(0, Math.min(rank - 4, Math.max(peers.length - 7, 0)))
return peers.slice(start, start + 7).map((item, index) => ({
model: item.model,
provider: item.provider,
author: formatProvider(item.provider),
rank: start + index + 1,
tokens: item.totalTokens,
share: totalTokens > 0 ? round((item.totalTokens / totalTokens) * 100, 2) : 0,
slug: modelSlug(item.model),
}))
}
function rowsForProduct<T extends { periodStart: number; tier: string }>(
rows: T[],
product: UsageProduct,
start: number,
end: number,
) {
const windowRows = rows.filter((row) => row.periodStart >= start && row.periodStart < end)
if (product !== "All Users") return windowRows.filter((row) => row.tier === product)
const allRows = windowRows.filter((row) => row.tier === "all")
if (allRows.length > 0) return allRows
return windowRows.filter((row) => row.tier !== "all")
}
function aggregateByModel(rows: StatMetricRow[]) {
return Object.values(
rows.reduce<Record<string, ModelAggregate>>((result, row) => {
const key = modelKey(row.provider, row.model)
result[key] = combineModelAggregate(result[key], row)
return result
}, {}),
)
}
function aggregateByModelName(rows: StatMetricRow[]) {
return Object.values(
rows.reduce<Record<string, ModelAggregate>>((result, row) => {
result[row.model] = combineModelAggregate(result[row.model], row)
return result
}, {}),
)
}
function aggregateByProvider(rows: ProviderMetricRow[]) {
return Object.values(
rows.reduce<Record<string, { provider: string; tokens: number }>>((result, row) => {
result[row.provider] = {
provider: row.provider,
tokens: (result[row.provider]?.tokens ?? 0) + row.totalTokens,
}
return result
}, {}),
)
}
function aggregateByCountry(rows: GeoMetricRow[]) {
return Object.values(
rows.reduce<Record<string, { country: string; continent: string; tokens: number }>>((result, row) => {
result[row.country] = {
country: row.country,
continent: result[row.country]?.continent || row.continent,
tokens: (result[row.country]?.tokens ?? 0) + row.totalTokens,
}
return result
}, {}),
)
}
function combineRowsForModel(model: string, rows: StatMetricRow[]): ModelAggregate {
const aggregate = rows.reduce<ModelAggregate | undefined>(
(result, row) => combineModelAggregate(result, row),
undefined,
)
if (aggregate) return { ...aggregate, model: model || aggregate.model }
return {
model,
provider: "unknown",
sessions: 0,
inputTokens: 0,
outputTokens: 0,
reasoningTokens: 0,
cacheReadTokens: 0,
totalTokens: 0,
inputCostMicrocents: 0,
outputCostMicrocents: 0,
totalCostMicrocents: 0,
}
}
function combineModelAggregate(current: ModelAggregate | undefined, row: StatMetricRow): ModelAggregate {
return {
model: row.model,
provider: row.provider,
sessions: (current?.sessions ?? 0) + row.sessions,
inputTokens: (current?.inputTokens ?? 0) + row.inputTokens,
outputTokens: (current?.outputTokens ?? 0) + row.outputTokens,
reasoningTokens: (current?.reasoningTokens ?? 0) + row.reasoningTokens,
cacheReadTokens: (current?.cacheReadTokens ?? 0) + row.cacheReadTokens,
totalTokens: (current?.totalTokens ?? 0) + row.totalTokens,
inputCostMicrocents: (current?.inputCostMicrocents ?? 0) + row.inputCostMicrocents,
outputCostMicrocents: (current?.outputCostMicrocents ?? 0) + row.outputCostMicrocents,
totalCostMicrocents: (current?.totalCostMicrocents ?? 0) + row.totalCostMicrocents,
}
}
function getWindow(range: UsageRange, earliest: number, latest: number): DateWindow {
const end = latest + DAY_MS
const start = Math.max(
earliest,
range === "1D"
? latest
: range === "1W"
? latest - 6 * DAY_MS
: range === "2W"
? latest - 13 * DAY_MS
: range === "1M"
? latest - 27 * DAY_MS
: range === "2M"
? latest - 55 * DAY_MS
: range === "3M"
? latest - 89 * DAY_MS
: range === "YTD"
? Date.UTC(new Date(latest).getUTCFullYear(), 0, 1)
: earliest,
)
const duration = end - start
return { start, end, previousStart: start - duration, previousEnd: start }
}
function createBuckets(window: DateWindow, range: UsageRange): Bucket[] {
const span = Math.max(window.end - window.start, DAY_MS)
const count =
range === "1D"
? 1
: range === "1W" || range === "2W" || range === "1M" || range === "2M" || range === "3M"
? Math.ceil(span / DAY_MS)
: Math.max(1, Math.min(7, Math.ceil(span / DAY_MS)))
const size = span / count
return Array.from({ length: count }, (_, index) => {
const start = window.start + index * size
const end = index === count - 1 ? window.end : window.start + (index + 1) * size
return { start, end, label: formatBucketLabel(start, end, range) }
})
}
function createUsageProductRecord<T>(value: (product: UsageProduct) => T): Record<UsageProduct, T> {
return {
"All Users": value("All Users"),
Zen: value("Zen"),
Go: value("Go"),
Enterprise: value("Enterprise"),
}
}
function createTokenProductRecord<T>(value: (product: TokenProduct) => T): Record<TokenProduct, T> {
return {
Zen: value("Zen"),
Go: value("Go"),
Enterprise: value("Enterprise"),
}
}
function createRangeRecord<T>(value: (range: UsageRange) => T): Record<UsageRange, T> {
return {
"1D": value("1D"),
"1W": value("1W"),
"2W": value("2W"),
"1M": value("1M"),
"2M": value("2M"),
"3M": value("3M"),
YTD: value("YTD"),
ALL: value("ALL"),
}
}
function normalizeStatRow(row: ModelStatMetric): StatMetricRow[] {
const periodStart = periodKeyTime(row.periodKey)
const updatedAt = dateTime(row.updatedAt)
if (!Number.isFinite(periodStart) || !Number.isFinite(updatedAt)) return []
return [
{
...row,
periodStart,
updatedAt,
tier: normalizeTier(row.tier),
provider: row.provider || "unknown",
model: row.model || "unknown",
},
]
}
function normalizeProviderRow(row: ProviderStatMetric): ProviderMetricRow[] {
const periodStart = periodKeyTime(row.periodKey)
const updatedAt = dateTime(row.updatedAt)
if (!Number.isFinite(periodStart) || !Number.isFinite(updatedAt)) return []
return [
{
...row,
periodStart,
updatedAt,
tier: normalizeTier(row.tier),
provider: row.provider || "unknown",
},
]
}
function normalizeGeoRow(row: GeoStatMetric): GeoMetricRow[] {
const periodStart = periodKeyTime(row.periodKey)
const updatedAt = dateTime(row.updatedAt)
if (!Number.isFinite(periodStart) || !Number.isFinite(updatedAt)) return []
return [
{
...row,
periodStart,
updatedAt,
tier: normalizeTier(row.tier),
provider: row.provider || "all",
model: row.model || "all",
country: row.country || "ZZ",
continent: row.continent || "",
},
]
}
function normalizeTier(value: string) {
const normalized = value.toLowerCase()
if (normalized === "paid" || normalized === "zen") return "Zen"
if (normalized === "go") return "Go"
if (normalized === "enterprise") return "Enterprise"
if (normalized === "all") return "all"
return value
}
function dateTime(value: Date | string) {
return (value instanceof Date ? value : new Date(value)).getTime()
}
function periodKeyTime(value: string) {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value)
if (!match) return Number.NaN
return Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]))
}
function formatBucketLabel(start: number, _end: number, range: UsageRange) {
const date = new Date(start)
if (range === "YTD") return months[date.getUTCMonth()]
if (range === "ALL")
return date.getUTCFullYear() === new Date().getUTCFullYear()
? months[date.getUTCMonth()]
: String(date.getUTCFullYear())
return formatDay(start)
}
function formatDay(value: number) {
const date = new Date(value)
return `${months[date.getUTCMonth()]} ${date.getUTCDate()}`
}
function formatProvider(provider: string) {
const known: Record<string, string> = {
anthropic: "Anthropic",
deepseek: "DeepSeek",
google: "Google",
minimax: "MiniMax",
moonshot: "Moonshot",
moonshotai: "Moonshot",
nvidia: "NVIDIA",
opencode: "opencode",
openai: "OpenAI",
qwen: "Qwen",
tencent: "Tencent",
xai: "xAI",
xiaomi: "Xiaomi",
zhipu: "Zhipu",
zhipuai: "Zhipu",
}
const normalized = provider.toLowerCase().replace(/[^a-z0-9]/g, "")
return known[normalized] ?? provider.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase())
}
function resolveModelName(modelParam: string, rows: StatMetricRow[], providerParam?: string) {
const input = modelParam.trim()
if (!input) return undefined
const normalizedInput = input.toLowerCase()
const inputSlug = modelSlug(input)
const candidates = providerParam
? aggregateByModel(rows).filter((item) => providerMatches(item.provider, providerParam))
: aggregateByModelName(rows)
return candidates
.filter((item) => item.model.toLowerCase() === normalizedInput || modelSlug(item.model) === inputSlug)
.toSorted((a, b) => b.totalTokens - a.totalTokens || a.model.localeCompare(b.model))[0]?.model
}
function resolveModelProvider(model: string, rows: StatMetricRow[], providerParam?: string) {
return aggregateByModel(rows)
.filter((item) => item.model === model && (!providerParam || providerMatches(item.provider, providerParam)))
.toSorted((a, b) => b.totalTokens - a.totalTokens || a.provider.localeCompare(b.provider))[0]?.provider
}
function providerMatches(provider: string, providerParam: string) {
return modelSlug(provider) === modelSlug(providerParam)
}
function resolveProviderName(providerParam: string, rows: StatMetricRow[]) {
const input = providerParam.trim()
if (!input) return undefined
const inputSlug = modelSlug(input)
return aggregateByModel(rows)
.filter((item) => modelSlug(item.provider) === inputSlug)
.toSorted((a, b) => b.totalTokens - a.totalTokens || a.provider.localeCompare(b.provider))[0]?.provider
}
export function modelSlug(value: string) {
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.replace(/-{2,}/g, "-")
}
function modelKey(provider: string, model: string) {
return `${provider}\u0000${model}`
}
function costPerMillion(costMicrocents: number, tokens: number) {
if (tokens <= 0 || costMicrocents <= 0) return 0
return round((microcentsToDollars(costMicrocents) / tokens) * TOKEN_SCALE, 2)
}
function microcentsToDollars(value: number) {
return value * DOLLARS_PER_MICROCENT
}
function percentChange(current: number, previous: number) {
if (previous <= 0) return current > 0 ? 100 : 0
return Math.round(((current - previous) / previous) * 100)
}
function leaderboardChange(current: number, previous: number) {
if (current <= 0) return 0
if (previous <= 0 || current >= previous * LEADERBOARD_CHANGE_MIN_MULTIPLE) return null
return percentChange(current, previous)
}
function round(value: number, digits: number) {
return Number(value.toFixed(digits))
}

View File

@@ -0,0 +1,99 @@
import { describe, expect, test } from "bun:test"
import { toGeoAggregate, toModelAggregate, toProviderAggregate } from "./inference"
import { modelAuthor, normalizeInferenceModel, statModel, statProvider } from "./model-normalization"
describe("inference stat normalization", () => {
test("normalizes model suffixes used by router/provider variants", () => {
expect(normalizeInferenceModel("deepseek-v4-flash-free")).toBe("deepseek-v4-flash")
expect(normalizeInferenceModel("deepseek-v4-flash:global")).toBe("deepseek-v4-flash")
expect(normalizeInferenceModel("mimo-v2.5-free")).toBe("mimo-v2.5")
expect(normalizeInferenceModel("nemotron-3-super-free")).toBe("nemotron-3-super")
expect(normalizeInferenceModel("mimo-v2.5-free:global")).toBe("mimo-v2.5")
})
test("maps normalized model ids to public authors", () => {
expect(modelAuthor("big-pickle")).toBe("unknown")
expect(modelAuthor("claude-sonnet-4-5")).toBe("anthropic")
expect(modelAuthor("deepseek-v4-pro")).toBe("deepseek")
expect(modelAuthor("gemini-3.5-flash")).toBe("google")
expect(modelAuthor("glm-5.1")).toBe("zhipu")
expect(modelAuthor("gpt-5.5-pro")).toBe("openai")
expect(modelAuthor("grok-build-0.1")).toBe("xai")
expect(modelAuthor("hy3-preview")).toBe("tencent")
expect(modelAuthor("kimi-k2.6")).toBe("moonshot")
expect(modelAuthor("mimo-v2-omni")).toBe("xiaomi")
expect(modelAuthor("minimax-m2.7")).toBe("minimax")
expect(modelAuthor("nemotron-3-super-free")).toBe("nvidia")
expect(modelAuthor("qwen3.7-max")).toBe("qwen")
expect(modelAuthor("alpha-gpt-next")).toBeUndefined()
})
test("uses provider.model to resolve opencode route providers", () => {
expect(statModel("big-pickle", "claude-sonnet-4-5")).toBe("claude-sonnet-4-5")
expect(statModel("big-pickle", "gpt-5-free")).toBe("gpt-5")
expect(statModel("big-pickle", "")).toBe("unknown")
expect(statProvider("big-pickle", "claude-sonnet-4-5", "opencode")).toBe("anthropic")
expect(statProvider("big-pickle", "gpt-5", "opencode")).toBe("openai")
expect(statProvider("big-pickle", "", "opencode")).toBe("unknown")
expect(statProvider("unknown", "", "custom-provider")).toBe("custom-provider")
})
test("model aggregates prefer provider.model and use normalized model", () => {
expect(toModelAggregate(aggregate("alpha-gpt-next", "openai"))).toEqual([])
expect(toModelAggregate(aggregate("deepseek-v4-flash-free", "not-public-provider"))).toMatchObject([
{
period_key: "2026-05-20",
provider: "deepseek",
model: "deepseek-v4-flash",
},
])
expect(
toModelAggregate({ ...aggregate("big-pickle", "opencode"), provider_model: "claude-sonnet-4-5" }),
).toMatchObject([
{
provider: "anthropic",
model: "claude-sonnet-4-5",
provider_model: "claude-sonnet-4-5",
},
])
})
test("provider aggregates never keep opencode as the provider", () => {
expect(toProviderAggregate({ ...aggregate("big-pickle", "opencode"), provider_model: "gpt-5" })).toMatchObject([
{ provider: "openai" },
])
expect(toProviderAggregate(aggregate("big-pickle", "opencode"))).toMatchObject([{ provider: "unknown" }])
})
test("geo aggregates never keep opencode or big-pickle dimensions", () => {
expect(toGeoAggregate({ ...aggregate("big-pickle", "opencode"), country: "US" })).toMatchObject([
{ provider: "unknown", model: "unknown", country: "US" },
])
})
test("model aggregates use ISO week period keys", () => {
expect(
toModelAggregate({
...aggregate("gpt-5.5-pro", "openai"),
grain: "week",
period_key: "2026-W20",
}),
).toMatchObject([{ period_key: "2026-W20" }])
})
})
function aggregate(model: string, provider: string) {
return {
grain: "day",
period_key: "2026-05-20",
dataset: "zen",
tier: "Paid",
provider,
model,
sessions: "1",
requests: "1",
sample_count: "1",
}
}

View File

@@ -0,0 +1,263 @@
import { Resource } from "sst/resource"
import type { AthenaData } from "../athena"
import type { GeoStatAggregate } from "./geo"
import type { ModelStatAggregate } from "./model"
import {
EXCLUDED_MODELS,
MODEL_AUTHOR_RULES,
RETIRED_STAT_PROVIDERS,
statModel,
statProvider,
} from "./model-normalization"
import type { ProviderStatAggregate } from "./provider"
import { normalizeCountry, normalizeTier, type StatBaseAggregate } from "./stat"
export type StatDimension = "model" | "provider" | "geo" | "geo_model"
export function buildStatsQuery(periodStart: Date, periodEnd: Date, dimension: StatDimension) {
const periodStartValue = sqlString(periodStart.toISOString())
const periodEndValue = sqlString(periodEnd.toISOString())
const sourceTable = [Resource.InferenceEvent.catalog, Resource.InferenceEvent.database, Resource.InferenceEvent.table]
.map(sqlIdentifier)
.join(".")
const dimensionSql = (() => {
if (dimension === "model")
return {
select: "provider, model, COALESCE(MAX(NULLIF(provider_model, '')), '') AS provider_model",
groupBy: "provider, model",
}
if (dimension === "provider") return { select: "provider", groupBy: "provider" }
if (dimension === "geo_model")
return {
select: "provider, model, country, COALESCE(MAX(NULLIF(continent, '')), '') AS continent",
groupBy: "provider, model, country",
}
return {
select: "'all' AS provider, 'all' AS model, country, COALESCE(MAX(NULLIF(continent, '')), '') AS continent",
groupBy: "country",
}
})()
const aggregateColumns = `
COUNT(DISTINCT session) AS sessions,
COUNT(*) AS requests,
COALESCE(SUM(tokens_input), 0) AS input_tokens,
COALESCE(SUM(tokens_output), 0) AS output_tokens,
COALESCE(SUM(tokens_reasoning), 0) AS reasoning_tokens,
COALESCE(SUM(tokens_cache_read), 0) AS cache_read_tokens,
COALESCE(SUM(tokens_total), 0) AS total_tokens,
COALESCE(SUM(cost_input_microcents), 0) AS input_cost_microcents,
COALESCE(SUM(cost_output_microcents), 0) AS output_cost_microcents,
COALESCE(SUM(cost_total_microcents), 0) AS total_cost_microcents,
AVG(duration_ms) AS avg_duration_ms,
approx_percentile(CAST(duration_ms AS double), 0.5) AS p50_duration_ms,
approx_percentile(CAST(duration_ms AS double), 0.95) AS p95_duration_ms,
AVG(ttfb_ms) AS avg_ttfb_ms,
approx_percentile(CAST(ttfb_ms AS double), 0.5) AS p50_ttfb_ms,
approx_percentile(CAST(ttfb_ms AS double), 0.95) AS p95_ttfb_ms,
AVG(output_tps) AS avg_output_tps,
SUM(CASE WHEN status >= 200 AND status < 400 THEN 1 ELSE 0 END) AS success_count,
SUM(CASE WHEN status >= 400 THEN 1 ELSE 0 END) AS error_count,
COUNT(*) AS sample_count`
return `
WITH normalized AS (
SELECT
from_iso8601_timestamp(event_timestamp) AS event_time,
model AS raw_model,
${statModelSql("model", "provider_model")} AS model,
COALESCE(NULLIF(provider_model, ''), '') AS provider_model,
COALESCE(NULLIF(provider, ''), '') AS raw_provider,
UPPER(COALESCE(NULLIF(cf_country, ''), 'ZZ')) AS country,
COALESCE(NULLIF(cf_continent, ''), '') AS continent,
session,
status,
duration AS duration_ms,
time_to_first_byte AS ttfb_ms,
timestamp_first_byte,
timestamp_last_byte,
tokens_input,
tokens_output,
tokens_reasoning,
tokens_cache_read,
tokens_cache_write_5m,
tokens_cache_write_1h,
cost_input_microcents,
cost_output_microcents,
cost_total_microcents,
cost_input,
cost_output,
cost_total,
source
FROM ${sourceTable}
WHERE event_type = 'completions'
AND model IS NOT NULL
AND model <> ''
AND event_timestamp >= ${periodStartValue}
AND event_timestamp < ${periodEndValue}
), filtered AS (
SELECT
event_time,
CASE
WHEN source = 'lite' THEN 'Go'
WHEN raw_model IN ('gpt-5-nano', 'grok-code', 'big-pickle') OR regexp_like(raw_model, '-free(:global)?$') THEN 'Free'
ELSE 'Paid'
END AS tier,
${statProviderSql("model", "provider_model", "raw_provider")} AS provider,
provider_model,
model,
country,
continent,
session,
status,
duration_ms,
ttfb_ms,
CASE
WHEN timestamp_last_byte - timestamp_first_byte < 100 THEN null
ELSE CAST(tokens_output AS double) / (timestamp_last_byte - timestamp_first_byte) * 1000
END AS output_tps,
tokens_input,
tokens_output,
tokens_reasoning,
tokens_cache_read,
COALESCE(tokens_cache_read, 0) + COALESCE(tokens_cache_write_5m, 0) + COALESCE(tokens_cache_write_1h, 0) + COALESCE(tokens_input, 0) + COALESCE(tokens_output, 0) AS tokens_total,
COALESCE(cost_input_microcents, cost_input * 1000000) AS cost_input_microcents,
COALESCE(cost_output_microcents, cost_output * 1000000) AS cost_output_microcents,
COALESCE(cost_total_microcents, cost_total * 1000000) AS cost_total_microcents
FROM normalized
WHERE lower(model) NOT IN (${[...EXCLUDED_MODELS].map(sqlString).join(", ")})
), weekly AS (
SELECT
concat(CAST(year_of_week(event_time) AS varchar), '-W', lpad(CAST(week(event_time) AS varchar), 2, '0')) AS week_key,
*
FROM filtered
), daily AS (
SELECT substr(to_iso8601(date_trunc('day', event_time)), 1, 10) AS day_key, *
FROM filtered
)
SELECT
'week' AS grain,
week_key AS period_key,
${sqlString(Resource.StatsSyncConfig.dataset)} AS dataset,
tier,
${dimensionSql.select},
${aggregateColumns}
FROM weekly
GROUP BY week_key, tier, ${dimensionSql.groupBy}
UNION ALL
SELECT
'day' AS grain,
day_key AS period_key,
${sqlString(Resource.StatsSyncConfig.dataset)} AS dataset,
tier,
${dimensionSql.select},
${aggregateColumns}
FROM daily
GROUP BY day_key, tier, ${dimensionSql.groupBy}
ORDER BY grain, period_key, total_tokens DESC
`
}
export function toModelAggregate(data: AthenaData): ModelStatAggregate[] {
const model = statModel(data.model, data.provider_model)
const provider = statProvider(model, data.provider_model, data.provider)
if (!provider) return []
return toStatBaseAggregate(data).flatMap((base) => [
{ ...base, provider, model, provider_model: data.provider_model || "" },
])
}
export function toProviderAggregate(data: AthenaData): ProviderStatAggregate[] {
return toStatBaseAggregate(data).flatMap((base) => [
{ ...base, provider: statProvider(data.model, data.provider_model, data.provider) || "unknown" },
])
}
export function toGeoAggregate(data: AthenaData): GeoStatAggregate[] {
return toStatBaseAggregate(data).flatMap((base) => [
{
...base,
provider: statProvider(data.model, data.provider_model, data.provider) || "all",
model: statModel(data.model || "all", data.provider_model),
country: normalizeCountry(data.country),
continent: data.continent || "",
},
])
}
function toStatBaseAggregate(data: AthenaData): StatBaseAggregate[] {
const grain = data.grain === "day" || data.grain === "week" ? data.grain : undefined
if (!grain || !data.period_key) return []
return [
{
grain,
period_key: data.period_key,
dataset: data.dataset || Resource.StatsSyncConfig.dataset,
tier: normalizeTier(data.tier || "unknown"),
sessions: integer(data, "sessions"),
requests: integer(data, "requests"),
input_tokens: integer(data, "input_tokens"),
output_tokens: integer(data, "output_tokens"),
reasoning_tokens: integer(data, "reasoning_tokens"),
cache_read_tokens: integer(data, "cache_read_tokens"),
total_tokens: integer(data, "total_tokens"),
input_cost_microcents: integer(data, "input_cost_microcents"),
output_cost_microcents: integer(data, "output_cost_microcents"),
total_cost_microcents: integer(data, "total_cost_microcents"),
avg_duration_ms: nullableNumber(data, "avg_duration_ms"),
p50_duration_ms: nullableInteger(data, "p50_duration_ms"),
p95_duration_ms: nullableInteger(data, "p95_duration_ms"),
avg_ttfb_ms: nullableNumber(data, "avg_ttfb_ms"),
p50_ttfb_ms: nullableInteger(data, "p50_ttfb_ms"),
p95_ttfb_ms: nullableInteger(data, "p95_ttfb_ms"),
avg_output_tps: nullableNumber(data, "avg_output_tps"),
success_count: integer(data, "success_count"),
error_count: integer(data, "error_count"),
sample_count: integer(data, "sample_count"),
},
]
}
function integer(data: AthenaData, key: string) {
return Math.round(number(data, key))
}
function nullableNumber(data: AthenaData, key: string) {
if (data[key] === undefined || data[key] === "") return null
return Number(number(data, key).toFixed(2))
}
function nullableInteger(data: AthenaData, key: string) {
if (data[key] === undefined || data[key] === "") return null
return Math.round(number(data, key))
}
function number(data: AthenaData, key: string) {
const value = Number(data[key])
return Number.isFinite(value) ? value : 0
}
function sqlIdentifier(value: string) {
return `"${value.replace(/"/g, '""')}"`
}
function sqlString(value: string) {
return `'${value.replace(/'/g, "''")}'`
}
function statModelSql(model: string, providerModel: string) {
return `COALESCE(NULLIF(regexp_replace(CASE
WHEN lower(${model}) = 'big-pickle' THEN NULLIF(${providerModel}, '')
ELSE ${model}
END, '(-free|:global)+$', ''), ''), 'unknown')`
}
function statProviderSql(model: string, providerModel: string, provider: string) {
return `CASE
${MODEL_AUTHOR_RULES.map((item) => ` WHEN strpos(lower(${providerModel}), ${sqlString(item.match)}) > 0 THEN ${sqlString(item.author)}`).join("\n")}
${MODEL_AUTHOR_RULES.map((item) => ` WHEN strpos(lower(${model}), ${sqlString(item.match)}) > 0 THEN ${sqlString(item.author)}`).join("\n")}
WHEN ${provider} <> '' AND lower(${provider}) NOT IN (${RETIRED_STAT_PROVIDERS.map(sqlString).join(", ")}) THEN ${provider}
ELSE 'unknown'
END`
}

View File

@@ -0,0 +1,49 @@
export const MODEL_AUTHOR_RULES = [
{ match: "claude", author: "anthropic" },
{ match: "gemini", author: "google" },
{ match: "deepseek", author: "deepseek" },
{ match: "glm", author: "zhipu" },
{ match: "gpt", author: "openai" },
{ match: "grok", author: "xai" },
{ match: "hy3", author: "tencent" },
{ match: "kimi", author: "moonshot" },
{ match: "mimo", author: "xiaomi" },
{ match: "minimax", author: "minimax" },
{ match: "nemotron", author: "nvidia" },
{ match: "qwen", author: "qwen" },
] as const
export const EXCLUDED_MODELS = new Set(["alpha-gpt-next"])
export const RETIRED_STAT_MODELS = ["big-pickle"]
export const RETIRED_STAT_PROVIDERS = ["opencode"]
export function normalizeInferenceModel(value: string | undefined) {
return (value || "unknown").replace(/(-free|:global)+$/, "") || "unknown"
}
export function modelAuthor(value: string | undefined) {
const model = normalizeInferenceModel(value).toLowerCase()
if (EXCLUDED_MODELS.has(model)) return undefined
return MODEL_AUTHOR_RULES.find((item) => model.includes(item.match))?.author ?? "unknown"
}
export function statModel(model: string | undefined, providerModel: string | undefined) {
const normalized = normalizeInferenceModel(model)
if (RETIRED_STAT_MODELS.includes(normalized.toLowerCase())) return normalizeInferenceModel(providerModel)
return normalized
}
export function statProvider(
model: string | undefined,
providerModel: string | undefined,
provider: string | undefined,
) {
const modelAuthorValue = modelAuthor(statModel(model, providerModel))
if (!modelAuthorValue) return undefined
const providerModelAuthor = modelAuthor(providerModel)
if (providerModelAuthor && providerModelAuthor !== "unknown") return providerModelAuthor
if (modelAuthorValue !== "unknown") return modelAuthorValue
if (provider && !RETIRED_STAT_PROVIDERS.includes(provider.toLowerCase())) return provider
return modelAuthorValue
}

View File

@@ -0,0 +1,202 @@
import { and, asc, eq, inArray, or } from "drizzle-orm"
import { Effect, Layer } from "effect"
import * as Context from "effect/Context"
import { DatabaseError, DrizzleClient } from "../database"
import { modelStat } from "../database/schema"
import { RETIRED_STAT_MODELS, RETIRED_STAT_PROVIDERS } from "./model-normalization"
import {
chunks,
collapseRows,
inserted,
rankBy,
statPeriodKey,
statRowScope,
synthesizeAllTierRows,
toStatBaseRow,
UPSERT_CHUNK_SIZE,
type StatBaseAggregate,
} from "./stat"
export type ModelStatRow = typeof modelStat.$inferInsert
export type ModelStatAggregate = StatBaseAggregate & { provider: string; model: string; provider_model: string }
export type ModelStatMetric = {
periodKey: string
updatedAt: Date
tier: string
provider: string
model: string
sessions: number
inputTokens: number
outputTokens: number
reasoningTokens: number
cacheReadTokens: number
totalTokens: number
inputCostMicrocents: number
outputCostMicrocents: number
totalCostMicrocents: number
}
export declare namespace ModelStatRepo {
export interface Service {
readonly listDaily: () => Effect.Effect<ModelStatMetric[], DatabaseError>
readonly upsert: (rows: ModelStatRow[]) => Effect.Effect<void, DatabaseError>
readonly deleteRetiredDimensions: (rows: ModelStatRow[]) => Effect.Effect<void, DatabaseError>
}
}
export class ModelStatRepo extends Context.Service<ModelStatRepo, ModelStatRepo.Service>()(
"@opencode/stats/ModelStatRepo",
) {
static readonly layer: Layer.Layer<ModelStatRepo, never, DrizzleClient> = Layer.effect(
ModelStatRepo,
Effect.gen(function* () {
const db = yield* DrizzleClient
const listDaily = Effect.fn("ModelStatRepo.listDaily")(function* () {
return yield* Effect.tryPromise({
try: () =>
db
.select({
periodKey: modelStat.period_key,
updatedAt: modelStat.updated_at,
tier: modelStat.tier,
provider: modelStat.provider,
model: modelStat.model,
sessions: modelStat.sessions,
inputTokens: modelStat.input_tokens,
outputTokens: modelStat.output_tokens,
reasoningTokens: modelStat.reasoning_tokens,
cacheReadTokens: modelStat.cache_read_tokens,
totalTokens: modelStat.total_tokens,
inputCostMicrocents: modelStat.input_cost_microcents,
outputCostMicrocents: modelStat.output_cost_microcents,
totalCostMicrocents: modelStat.total_cost_microcents,
})
.from(modelStat)
.where(and(eq(modelStat.grain, "day"), eq(modelStat.client, "all"), eq(modelStat.source, "all")))
.orderBy(asc(modelStat.period_key)),
catch: (cause) => DatabaseError.make({ cause }),
})
})
const upsert = Effect.fn("ModelStatRepo.upsert")(function* (rows: ModelStatRow[]) {
yield* Effect.forEach(
chunks(rows, UPSERT_CHUNK_SIZE),
(chunk) =>
Effect.tryPromise({
try: () =>
db
.insert(modelStat)
.values(chunk)
.onDuplicateKeyUpdate({
set: {
provider_model: inserted("provider_model"),
sessions: inserted("sessions"),
requests: inserted("requests"),
input_tokens: inserted("input_tokens"),
output_tokens: inserted("output_tokens"),
reasoning_tokens: inserted("reasoning_tokens"),
cache_read_tokens: inserted("cache_read_tokens"),
total_tokens: inserted("total_tokens"),
input_cost_microcents: inserted("input_cost_microcents"),
output_cost_microcents: inserted("output_cost_microcents"),
total_cost_microcents: inserted("total_cost_microcents"),
avg_duration_ms: inserted("avg_duration_ms"),
p50_duration_ms: inserted("p50_duration_ms"),
p95_duration_ms: inserted("p95_duration_ms"),
avg_ttfb_ms: inserted("avg_ttfb_ms"),
p50_ttfb_ms: inserted("p50_ttfb_ms"),
p95_ttfb_ms: inserted("p95_ttfb_ms"),
avg_output_tps: inserted("avg_output_tps"),
success_count: inserted("success_count"),
error_count: inserted("error_count"),
sample_count: inserted("sample_count"),
rank_by_tokens: inserted("rank_by_tokens"),
rank_by_requests: inserted("rank_by_requests"),
rank_by_cost: inserted("rank_by_cost"),
},
}),
catch: (cause) => DatabaseError.make({ cause }),
}),
{ discard: true },
)
})
const deleteRetiredDimensions = Effect.fn("ModelStatRepo.deleteRetiredDimensions")(function* (
rows: ModelStatRow[],
) {
const scope = statRowScope(rows)
if (!scope) return
yield* Effect.tryPromise({
try: () =>
db
.delete(modelStat)
.where(
and(
inArray(modelStat.grain, scope.grains),
inArray(modelStat.period_key, scope.periodKeys),
inArray(modelStat.dataset, scope.datasets),
inArray(modelStat.client, scope.clients),
inArray(modelStat.source, scope.sources),
or(
inArray(modelStat.provider, RETIRED_STAT_PROVIDERS),
inArray(modelStat.model, RETIRED_STAT_MODELS),
),
),
),
catch: (cause) => DatabaseError.make({ cause }),
})
})
return ModelStatRepo.of({ listDaily, upsert, deleteRetiredDimensions })
}),
)
}
export function rowsFromAggregates(aggregates: ModelStatAggregate[]) {
return rankRows([
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "week").map(toRow), dimensionKey),
dimensionKey,
),
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "day").map(toRow), dimensionKey),
dimensionKey,
),
])
}
function toRow(data: ModelStatAggregate): ModelStatRow {
return {
...toStatBaseRow(data),
provider: data.provider,
model: data.model,
provider_model: data.provider_model,
}
}
function rankRows(rows: ModelStatRow[]) {
return Object.values(
rows.reduce<Record<string, ModelStatRow[]>>((result, row) => {
const key = statPeriodKey(row)
result[key] = [...(result[key] ?? []), row]
return result
}, {}),
).flatMap((group) => {
const tokenRanks = rankBy(group, (row) => row.total_tokens ?? 0)
const requestRanks = rankBy(group, (row) => row.requests ?? 0)
const costRanks = rankBy(group, (row) => row.total_cost_microcents ?? 0)
return group.map((row) => ({
...row,
rank_by_tokens: tokenRanks.get(row) ?? null,
rank_by_requests: requestRanks.get(row) ?? null,
rank_by_cost: costRanks.get(row) ?? null,
}))
})
}
function dimensionKey(row: ModelStatRow) {
return [row.provider, row.model].join("\u0000")
}

View File

@@ -0,0 +1,195 @@
import { and, asc, eq, inArray } from "drizzle-orm"
import { Effect, Layer } from "effect"
import * as Context from "effect/Context"
import { DatabaseError, DrizzleClient } from "../database"
import { providerStat } from "../database/schema"
import { RETIRED_STAT_PROVIDERS } from "./model-normalization"
import {
chunks,
collapseRows,
inserted,
rankRowsWithMarketShare,
statRowScope,
synthesizeAllTierRows,
toStatBaseRow,
UPSERT_CHUNK_SIZE,
type StatBaseAggregate,
} from "./stat"
export type ProviderStatRow = typeof providerStat.$inferInsert
export type ProviderStatAggregate = StatBaseAggregate & { provider: string }
export type ProviderStatMetric = {
periodKey: string
updatedAt: Date
tier: string
provider: string
totalTokens: number
}
export declare namespace ProviderStatRepo {
export interface Service {
readonly listDaily: () => Effect.Effect<ProviderStatMetric[], DatabaseError>
readonly listByPeriod: (opts: {
readonly grain: string
readonly periodKey: string
readonly dataset?: string
readonly tier?: string
readonly client?: string
readonly source?: string
}) => Effect.Effect<ProviderStatRow[], DatabaseError>
readonly upsert: (rows: ProviderStatRow[]) => Effect.Effect<void, DatabaseError>
readonly deleteRetiredDimensions: (rows: ProviderStatRow[]) => Effect.Effect<void, DatabaseError>
}
}
export class ProviderStatRepo extends Context.Service<ProviderStatRepo, ProviderStatRepo.Service>()(
"@opencode/stats/ProviderStatRepo",
) {
static readonly layer: Layer.Layer<ProviderStatRepo, never, DrizzleClient> = Layer.effect(
ProviderStatRepo,
Effect.gen(function* () {
const db = yield* DrizzleClient
const listDaily = Effect.fn("ProviderStatRepo.listDaily")(function* () {
return yield* Effect.tryPromise({
try: () =>
db
.select({
periodKey: providerStat.period_key,
updatedAt: providerStat.updated_at,
tier: providerStat.tier,
provider: providerStat.provider,
totalTokens: providerStat.total_tokens,
})
.from(providerStat)
.where(and(eq(providerStat.grain, "day"), eq(providerStat.client, "all"), eq(providerStat.source, "all")))
.orderBy(asc(providerStat.period_key)),
catch: (cause) => DatabaseError.make({ cause }),
})
})
const listByPeriod = Effect.fn("ProviderStatRepo.listByPeriod")(function* (opts: {
readonly grain: string
readonly periodKey: string
readonly dataset?: string
readonly tier?: string
readonly client?: string
readonly source?: string
}) {
return yield* Effect.tryPromise({
try: () =>
db
.select()
.from(providerStat)
.where(
and(
eq(providerStat.grain, opts.grain),
eq(providerStat.period_key, opts.periodKey),
eq(providerStat.dataset, opts.dataset ?? "zen"),
eq(providerStat.tier, opts.tier ?? "all"),
eq(providerStat.client, opts.client ?? "all"),
eq(providerStat.source, opts.source ?? "all"),
),
),
catch: (cause) => DatabaseError.make({ cause }),
})
})
const upsert = Effect.fn("ProviderStatRepo.upsert")(function* (rows: ProviderStatRow[]) {
yield* Effect.forEach(
chunks(rows, UPSERT_CHUNK_SIZE),
(chunk) =>
Effect.tryPromise({
try: () =>
db
.insert(providerStat)
.values(chunk)
.onDuplicateKeyUpdate({
set: {
sessions: inserted("sessions"),
requests: inserted("requests"),
input_tokens: inserted("input_tokens"),
output_tokens: inserted("output_tokens"),
reasoning_tokens: inserted("reasoning_tokens"),
cache_read_tokens: inserted("cache_read_tokens"),
total_tokens: inserted("total_tokens"),
input_cost_microcents: inserted("input_cost_microcents"),
output_cost_microcents: inserted("output_cost_microcents"),
total_cost_microcents: inserted("total_cost_microcents"),
avg_duration_ms: inserted("avg_duration_ms"),
p50_duration_ms: inserted("p50_duration_ms"),
p95_duration_ms: inserted("p95_duration_ms"),
avg_ttfb_ms: inserted("avg_ttfb_ms"),
p50_ttfb_ms: inserted("p50_ttfb_ms"),
p95_ttfb_ms: inserted("p95_ttfb_ms"),
avg_output_tps: inserted("avg_output_tps"),
success_count: inserted("success_count"),
error_count: inserted("error_count"),
sample_count: inserted("sample_count"),
market_share_tokens: inserted("market_share_tokens"),
market_share_requests: inserted("market_share_requests"),
market_share_sessions: inserted("market_share_sessions"),
rank_by_tokens: inserted("rank_by_tokens"),
rank_by_requests: inserted("rank_by_requests"),
rank_by_sessions: inserted("rank_by_sessions"),
rank_by_cost: inserted("rank_by_cost"),
},
}),
catch: (cause) => DatabaseError.make({ cause }),
}),
{ discard: true },
)
})
const deleteRetiredDimensions = Effect.fn("ProviderStatRepo.deleteRetiredDimensions")(function* (
rows: ProviderStatRow[],
) {
const scope = statRowScope(rows)
if (!scope) return
yield* Effect.tryPromise({
try: () =>
db
.delete(providerStat)
.where(
and(
inArray(providerStat.grain, scope.grains),
inArray(providerStat.period_key, scope.periodKeys),
inArray(providerStat.dataset, scope.datasets),
inArray(providerStat.client, scope.clients),
inArray(providerStat.source, scope.sources),
inArray(providerStat.provider, RETIRED_STAT_PROVIDERS),
),
),
catch: (cause) => DatabaseError.make({ cause }),
})
})
return ProviderStatRepo.of({ listDaily, listByPeriod, upsert, deleteRetiredDimensions })
}),
)
}
export function rowsFromAggregates(aggregates: ProviderStatAggregate[]) {
return rankRowsWithMarketShare([
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "week").map(toRow), dimensionKey),
dimensionKey,
),
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "day").map(toRow), dimensionKey),
dimensionKey,
),
])
}
function toRow(data: ProviderStatAggregate): ProviderStatRow {
return {
...toStatBaseRow(data),
provider: data.provider,
}
}
function dimensionKey(row: ProviderStatRow) {
return row.provider
}

View File

@@ -0,0 +1,261 @@
import { sql } from "drizzle-orm"
export const UPSERT_CHUNK_SIZE = 500
const DAY_MS = 86_400_000
export type StatGrain = "day" | "week"
export type StatBaseAggregate = {
grain: StatGrain
period_key: string
dataset: string
tier: string
sessions: number
requests: number
input_tokens: number
output_tokens: number
reasoning_tokens: number
cache_read_tokens: number
total_tokens: number
input_cost_microcents: number
output_cost_microcents: number
total_cost_microcents: number
avg_duration_ms: number | null
p50_duration_ms: number | null
p95_duration_ms: number | null
avg_ttfb_ms: number | null
p50_ttfb_ms: number | null
p95_ttfb_ms: number | null
avg_output_tps: number | null
success_count: number
error_count: number
sample_count: number
}
export type StatBaseRow = {
grain: string
period_key: string
dataset?: string
tier?: string
client?: string
source?: string
sessions?: number
requests?: number
input_tokens?: number
output_tokens?: number
reasoning_tokens?: number
cache_read_tokens?: number
total_tokens?: number
input_cost_microcents?: number
output_cost_microcents?: number
total_cost_microcents?: number
avg_duration_ms?: number | null
p50_duration_ms?: number | null
p95_duration_ms?: number | null
avg_ttfb_ms?: number | null
p50_ttfb_ms?: number | null
p95_ttfb_ms?: number | null
avg_output_tps?: number | null
success_count?: number
error_count?: number
sample_count?: number
}
export function toStatBaseRow(data: StatBaseAggregate) {
return {
grain: data.grain,
period_key: data.period_key,
dataset: data.dataset,
tier: data.tier,
client: "all",
source: "all",
sessions: data.sessions,
requests: data.requests,
input_tokens: data.input_tokens,
output_tokens: data.output_tokens,
reasoning_tokens: data.reasoning_tokens,
cache_read_tokens: data.cache_read_tokens,
total_tokens: data.total_tokens,
input_cost_microcents: data.input_cost_microcents,
output_cost_microcents: data.output_cost_microcents,
total_cost_microcents: data.total_cost_microcents,
avg_duration_ms: data.avg_duration_ms,
p50_duration_ms: data.p50_duration_ms,
p95_duration_ms: data.p95_duration_ms,
avg_ttfb_ms: data.avg_ttfb_ms,
p50_ttfb_ms: data.p50_ttfb_ms,
p95_ttfb_ms: data.p95_ttfb_ms,
avg_output_tps: data.avg_output_tps,
success_count: data.success_count,
error_count: data.error_count,
sample_count: data.sample_count,
}
}
export function synthesizeAllTierRows<T extends StatBaseRow>(rows: T[], dimensionKey: (row: T) => string) {
return [
...rows,
...Object.values(
rows.reduce<Record<string, T>>((result, row) => {
const key = [row.grain, row.period_key, row.dataset, row.client, row.source, dimensionKey(row)].join("\u0000")
result[key] = result[key] ? combineRows(result[key], row) : { ...row, tier: "all" }
return result
}, {}),
),
]
}
export function collapseRows<T extends StatBaseRow>(rows: T[], dimensionKey: (row: T) => string) {
return Object.values(
rows.reduce<Record<string, T>>((result, row) => {
const key = [row.grain, row.period_key, row.dataset, row.tier, row.client, row.source, dimensionKey(row)].join(
"\u0000",
)
result[key] = result[key] ? combineRows(result[key], row) : row
return result
}, {}),
)
}
export function combineRows<T extends StatBaseRow>(left: T, right: T): T {
return {
...left,
sessions: (left.sessions ?? 0) + (right.sessions ?? 0),
requests: (left.requests ?? 0) + (right.requests ?? 0),
input_tokens: (left.input_tokens ?? 0) + (right.input_tokens ?? 0),
output_tokens: (left.output_tokens ?? 0) + (right.output_tokens ?? 0),
reasoning_tokens: (left.reasoning_tokens ?? 0) + (right.reasoning_tokens ?? 0),
cache_read_tokens: (left.cache_read_tokens ?? 0) + (right.cache_read_tokens ?? 0),
total_tokens: (left.total_tokens ?? 0) + (right.total_tokens ?? 0),
input_cost_microcents: (left.input_cost_microcents ?? 0) + (right.input_cost_microcents ?? 0),
output_cost_microcents: (left.output_cost_microcents ?? 0) + (right.output_cost_microcents ?? 0),
total_cost_microcents: (left.total_cost_microcents ?? 0) + (right.total_cost_microcents ?? 0),
avg_duration_ms: weightedAverage(left.avg_duration_ms, left.requests, right.avg_duration_ms, right.requests),
p50_duration_ms: null,
p95_duration_ms: null,
avg_ttfb_ms: weightedAverage(left.avg_ttfb_ms, left.requests, right.avg_ttfb_ms, right.requests),
p50_ttfb_ms: null,
p95_ttfb_ms: null,
avg_output_tps: weightedAverage(left.avg_output_tps, left.requests, right.avg_output_tps, right.requests),
success_count: (left.success_count ?? 0) + (right.success_count ?? 0),
error_count: (left.error_count ?? 0) + (right.error_count ?? 0),
sample_count: (left.sample_count ?? 0) + (right.sample_count ?? 0),
}
}
export function statPeriodKey(row: StatBaseRow) {
return [row.grain, row.period_key, row.dataset, row.tier, row.client, row.source].join("\u0000")
}
export function statRowScope(rows: StatBaseRow[]) {
if (rows.length === 0) return
return {
grains: unique(rows.map((row) => row.grain)),
periodKeys: unique(rows.map((row) => row.period_key)),
datasets: unique(rows.map((row) => row.dataset ?? "all")),
clients: unique(rows.map((row) => row.client ?? "all")),
sources: unique(rows.map((row) => row.source ?? "all")),
}
}
export function periodKeyFor(grain: StatGrain, periodStart: Date) {
if (grain === "week") return isoWeekId(periodStart)
return utcDateId(periodStart)
}
export function startOfUtcDay(value: Date) {
return new Date(Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate()))
}
export function startOfIsoWeek(value: Date) {
return new Date(
Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate() - (value.getUTCDay() || 7) + 1),
)
}
export function isoWeekId(value: Date) {
const thursday = new Date(
Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate() + 4 - (value.getUTCDay() || 7)),
)
return `${thursday.getUTCFullYear()}-W${String(Math.ceil(((thursday.getTime() - Date.UTC(thursday.getUTCFullYear(), 0, 1)) / DAY_MS + 1) / 7)).padStart(2, "0")}`
}
function utcDateId(value: Date) {
return `${value.getUTCFullYear()}-${String(value.getUTCMonth() + 1).padStart(2, "0")}-${String(value.getUTCDate()).padStart(2, "0")}`
}
export function rankBy<T extends StatBaseRow>(rows: T[], value: (row: T) => number) {
return new Map(rows.toSorted((a, b) => value(b) - value(a)).map((row, index) => [row, index + 1]))
}
export function rankRowsWithMarketShare<T extends StatBaseRow>(
rows: T[],
groupKey: (row: T) => string = statPeriodKey,
) {
return Object.values(
rows.reduce<Record<string, T[]>>((result, row) => {
const key = groupKey(row)
result[key] = [...(result[key] ?? []), row]
return result
}, {}),
).flatMap((group) => {
const tokens = group.reduce((sum, row) => sum + (row.total_tokens ?? 0), 0)
const requests = group.reduce((sum, row) => sum + (row.requests ?? 0), 0)
const sessions = group.reduce((sum, row) => sum + (row.sessions ?? 0), 0)
const tokenRanks = rankBy(group, (row) => row.total_tokens ?? 0)
const requestRanks = rankBy(group, (row) => row.requests ?? 0)
const sessionRanks = rankBy(group, (row) => row.sessions ?? 0)
const costRanks = rankBy(group, (row) => row.total_cost_microcents ?? 0)
return group.map((row) => ({
...row,
market_share_tokens: share(row.total_tokens, tokens),
market_share_requests: share(row.requests, requests),
market_share_sessions: share(row.sessions, sessions),
rank_by_tokens: tokenRanks.get(row) ?? null,
rank_by_requests: requestRanks.get(row) ?? null,
rank_by_sessions: sessionRanks.get(row) ?? null,
rank_by_cost: costRanks.get(row) ?? null,
}))
})
}
export function share(value: number | null | undefined, total: number) {
if (total <= 0) return null
return Number(((value ?? 0) / total).toFixed(6))
}
export function chunks<T>(items: T[], size: number) {
return Array.from({ length: Math.ceil(items.length / size) }, (_, index) =>
items.slice(index * size, (index + 1) * size),
)
}
function unique(values: string[]) {
return [...new Set(values)]
}
export function inserted(column: string) {
return sql.raw(`values(\`${column}\`)`)
}
export function weightedAverage(
left: number | null | undefined,
leftWeight = 0,
right: number | null | undefined,
rightWeight = 0,
) {
const totalWeight =
(left === null || left === undefined ? 0 : leftWeight) + (right === null || right === undefined ? 0 : rightWeight)
if (totalWeight === 0) return null
return Number((((left ?? 0) * leftWeight + (right ?? 0) * rightWeight) / totalWeight).toFixed(2))
}
export function normalizeTier(value: string) {
if (value === "Paid") return "Zen"
return value
}
export function normalizeCountry(value: string | undefined) {
if (!value || value.length !== 2) return "ZZ"
return value.toUpperCase()
}

View File

@@ -0,0 +1,993 @@
import { Client } from "@planetscale/database"
import { readdir } from "node:fs/promises"
import path from "node:path"
import { drizzle } from "drizzle-orm/planetscale-serverless"
import { geoStat, modelStat, providerStat } from "./database/schema"
import { statModel, statProvider } from "./domain/model-normalization"
import {
chunks,
collapseRows,
inserted,
isoWeekId,
normalizeCountry,
normalizeTier,
periodKeyFor,
rankBy,
rankRowsWithMarketShare,
startOfIsoWeek,
startOfUtcDay,
statPeriodKey,
synthesizeAllTierRows,
toStatBaseRow,
type StatBaseAggregate,
} from "./domain/stat"
const DAY_MS = 86_400_000
const DEFAULT_UPSERT_CHUNK_SIZE = 100
const DEFAULT_TIERS = ["Go", "Free", "Paid"]
const FREE_MODELS = new Set(["gpt-5-nano", "grok-code", "big-pickle"])
type Grain = "day" | "week"
type MetricDimension = "model" | "provider" | "geo" | "geo-model"
type LookupDimension = "model-provider-model" | "geo-continent"
type ImportKey = `${MetricDimension | LookupDimension}-${Grain}`
type QuerySpec = {
name: string
importKey: ImportKey
importFlag: `--${ImportKey}`
query: ReturnType<typeof metricQuery>
}
type RawRow = Record<string, string>
type ImportOptions = {
dataset: string
databaseUrl: string | undefined
directories: string[]
dryRun: boolean
periodStart: Date | undefined
upsertChunkSize: number
files: Partial<Record<ImportKey, string[]>>
}
type ModelAggregate = StatBaseAggregate & { provider: string; model: string; provider_model: string }
type ProviderAggregate = StatBaseAggregate & { provider: string }
type GeoAggregate = StatBaseAggregate & { provider: string; model: string; country: string; continent: string }
type ModelStatRow = typeof modelStat.$inferInsert
type ProviderStatRow = typeof providerStat.$inferInsert
type GeoStatRow = typeof geoStat.$inferInsert
const inputKeys = [
"model-day",
"model-week",
"model-provider-model-day",
"model-provider-model-week",
"provider-day",
"provider-week",
"geo-day",
"geo-week",
"geo-model-day",
"geo-model-week",
"geo-continent-day",
"geo-continent-week",
] as const satisfies ImportKey[]
if (import.meta.main) await main()
async function main() {
const command = process.argv[2]
if (command === "queries") return printQueries(process.argv.slice(3))
if (command === "import") return importFiles(process.argv.slice(3))
usage()
}
function printQueries(args: string[]) {
const flags = parseFlags(args)
const limit = parseIntegerFlag(flags, "limit") ?? 1000
const tiers = parseListFlag(flags, "tiers") ?? DEFAULT_TIERS
const queries = buildQueries(limit, tiers)
const only = flags.get("only")?.[0]
if (only) {
const item = queries.find((query) => query.name === only)
if (!item) fail(`Unknown --only ${only}. Expected one of: ${queries.map((query) => query.name).join(", ")}`)
console.log(JSON.stringify(item.query, null, 2))
return
}
console.log(
JSON.stringify(
{
tiers,
import_hint: "bun src/honeycomb-backfill.ts import --dir downloads",
queries,
},
null,
2,
),
)
}
async function importFiles(args: string[]) {
const parsed = parseImportOptions(args)
const opts = { ...parsed, files: mergeFiles(parsed.files, await discoverFiles(parsed.directories)) }
if (!inputKeys.some((key) => opts.files[key]?.length)) fail("No CSV or JSON import files were provided or discovered")
const providerModelLookup = new Map([
...(await lookupRows(opts.files["model-provider-model-day"], "day", opts, modelProviderModelLookup)),
...(await lookupRows(opts.files["model-provider-model-week"], "week", opts, modelProviderModelLookup)),
])
const continentLookup = new Map([
...(await lookupRows(opts.files["geo-continent-day"], "day", opts, geoContinentLookup)),
...(await lookupRows(opts.files["geo-continent-week"], "week", opts, geoContinentLookup)),
])
const modelAggregates = [
...(await metricRows(opts.files["model-day"], "day", opts, (row, base) =>
modelAggregate(row, base, providerModelLookup),
)),
...(await metricRows(opts.files["model-week"], "week", opts, (row, base) =>
modelAggregate(row, base, providerModelLookup),
)),
]
const modelRows = modelRowsFromAggregates(modelAggregates)
const providerRows = providerRowsFromAggregates([
...(await metricRows(opts.files["provider-day"], "day", opts, (row, base) => ({
...base,
provider: provider(row) ?? "unknown",
}))),
...(await metricRows(opts.files["provider-week"], "week", opts, (row, base) => ({
...base,
provider: provider(row) ?? "unknown",
}))),
])
const geoRows = geoRowsFromAggregates([
...(await metricRows(opts.files["geo-day"], "day", opts, (row, base) => ({
...base,
provider: "all",
model: "all",
country: country(row),
continent: continentLookup.get(lookupKey(base, country(row))) ?? continent(row),
}))),
...(await metricRows(opts.files["geo-week"], "week", opts, (row, base) => ({
...base,
provider: "all",
model: "all",
country: country(row),
continent: continentLookup.get(lookupKey(base, country(row))) ?? continent(row),
}))),
...(await metricRows(opts.files["geo-model-day"], "day", opts, (row, base) =>
geoModelAggregate(row, base, continentLookup),
)),
...(await metricRows(opts.files["geo-model-week"], "week", opts, (row, base) =>
geoModelAggregate(row, base, continentLookup),
)),
])
console.log(
JSON.stringify(
{
inputs: Object.fromEntries(
inputKeys.flatMap((key) => (opts.files[key]?.length ? [[key, opts.files[key].length]] : [])),
),
modelRows: modelRows.length,
providerRows: providerRows.length,
geoRows: geoRows.length,
dryRun: opts.dryRun,
upsertChunkSize: opts.upsertChunkSize,
},
null,
2,
),
)
if (opts.dryRun) return
if (!opts.databaseUrl) fail("DATABASE_URL is required unless --dry-run is set")
const db = drizzle({ client: new Client({ url: opts.databaseUrl }) })
await upsertModelRows(db, modelRows, opts.upsertChunkSize)
await upsertProviderRows(db, providerRows, opts.upsertChunkSize)
await upsertGeoRows(db, geoRows, opts.upsertChunkSize)
}
function buildQueries(limit: number, tiers: string[]): QuerySpec[] {
const daily = tiers.flatMap((tier) => [
querySpec(
"model-day",
tier,
metricQuery(["date", "tier", "stat_provider_2", "stat_model_2"], limit, tierFilters(tier)),
),
querySpec("provider-day", tier, metricQuery(["date", "tier", "stat_provider_2"], limit, tierFilters(tier))),
querySpec("geo-day", tier, metricQuery(["date", "tier", "country", "continent"], limit, tierFilters(tier))),
querySpec(
"geo-model-day",
tier,
metricQuery(
["date", "tier", "stat_provider_2", "stat_model_2", "country", "continent"],
limit,
tierFilters(tier),
),
),
])
const weekly = tiers.flatMap((tier) => [
querySpec(
"model-week",
tier,
metricQuery(["week", "tier", "stat_provider_2", "stat_model_2"], limit, tierFilters(tier)),
),
querySpec("provider-week", tier, metricQuery(["week", "tier", "stat_provider_2"], limit, tierFilters(tier))),
querySpec("geo-week", tier, metricQuery(["week", "tier", "country", "continent"], limit, tierFilters(tier))),
querySpec(
"geo-model-week",
tier,
metricQuery(
["week", "tier", "stat_provider_2", "stat_model_2", "country", "continent"],
limit,
tierFilters(tier),
),
),
])
return [...daily, ...weekly]
}
function querySpec(importKey: ImportKey, tier: string, query: ReturnType<typeof metricQuery>) {
return {
name: `${importKey}-${queryNameSegment(tier)}`,
importKey,
importFlag: `--${importKey}` as const,
query,
}
}
function metricQuery(breakdowns: string[], limit: number, filters: ReturnType<typeof commonFilters> = []) {
return {
granularity: 0,
breakdowns,
calculations: [
{ op: "COUNT_DISTINCT", column: "session" },
{ op: "COUNT" },
{ op: "SUM", column: "tokens.input" },
{ op: "SUM", column: "tokens.output" },
{ op: "SUM", column: "tokens.reasoning" },
{ op: "SUM", column: "tokens.cache_read" },
{ op: "SUM", column: "tokens" },
{ op: "SUM", column: "cost.input.microcents" },
{ op: "SUM", column: "cost.output.microcents" },
{ op: "SUM", column: "cost.total.microcents" },
{ op: "AVG", column: "duration" },
{ op: "P50", column: "duration" },
{ op: "P95", column: "duration" },
{ op: "AVG", column: "time_to_first_byte" },
{ op: "P50", column: "time_to_first_byte" },
{ op: "P95", column: "time_to_first_byte" },
{ op: "AVG", column: "tps.output" },
],
filters: [...commonFilters(), ...filters],
filter_combination: "AND",
orders: [{ column: "tokens", op: "SUM", order: "descending" }],
havings: [],
limit,
formulas: [],
}
}
function tierFilters(tier: string) {
if (tier === "all") return []
return [{ column: "tier", op: "=", value: tier }]
}
function queryNameSegment(value: string) {
return (
value
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "") || "all"
)
}
function commonFilters() {
return [
{ column: "event_type", op: "=", value: "completions" },
{ column: "model", op: "exists" },
{ column: "model", op: "!=", value: "" },
{ column: "model", op: "!=", value: "alpha-gpt-next" },
]
}
function metricRows<T extends StatBaseAggregate>(
files: string[] | undefined,
grain: Grain,
opts: ImportOptions,
map: (row: RawRow, base: StatBaseAggregate) => T | T[],
) {
if (!files) return Promise.resolve([])
return readFiles(files).then((rows) => rows.flatMap((row) => map(row, baseAggregate(row, grain, opts))))
}
function lookupRows(
files: string[] | undefined,
grain: Grain,
opts: ImportOptions,
map: (row: RawRow, grain: Grain, opts: ImportOptions) => readonly (readonly [string, string])[],
) {
if (!files) return Promise.resolve([])
return readFiles(files).then((rows) =>
Array.from(
rows
.flatMap((row) => map(row, grain, opts))
.reduce((result, [key, value]) => {
if (value && value > (result.get(key) ?? "")) result.set(key, value)
return result
}, new Map<string, string>()),
),
)
}
async function readFiles(files: string[]) {
return (await Promise.all(files.map(readRows))).flat()
}
async function discoverFiles(directories: string[]) {
const classified = await Promise.all(
(await Promise.all(directories.map(filesInDirectory))).flat().map(async (file) => ({
file,
key: classifyRows(file, await readRows(file)),
})),
)
return classified.reduce<Partial<Record<ImportKey, string[]>>>((result, item) => {
return { ...result, [item.key]: [...(result[item.key] ?? []), item.file] }
}, {})
}
async function filesInDirectory(directory: string): Promise<string[]> {
return (
await Promise.all(
(await readdir(directory, { withFileTypes: true })).map((entry) => {
const file = path.join(directory, entry.name)
if (entry.isDirectory()) return filesInDirectory(file)
if (entry.isFile() && /\.(csv|json)$/i.test(entry.name)) return Promise.resolve([file])
return Promise.resolve([])
}),
)
).flat()
}
function classifyRows(file: string, rows: RawRow[]): ImportKey {
if (rows.length === 0) fail(`Cannot classify empty export: ${file}`)
const headers = new Set(rows.flatMap((row) => Object.keys(row).map(normalizeHeader)))
const grain: Grain = headers.has("date") ? "day" : "week"
if (hasHeader(headers, ["country", "cf.country"])) {
if (hasHeader(headers, ["model", "stat_model", "stat_model_2"]) && hasMetricHeaders(headers))
return `geo-model-${grain}`
return hasMetricHeaders(headers) ? `geo-${grain}` : `geo-continent-${grain}`
}
if (hasHeader(headers, ["model", "stat_model", "stat_model_2"]))
return hasMetricHeaders(headers) ? `model-${grain}` : `model-provider-model-${grain}`
if (
hasHeader(headers, [
"provider",
"provider.normalized",
"stat_provider",
"stat_provider_2",
"provider.model",
"provider_model",
])
)
return `provider-${grain}`
fail(`Cannot classify export from columns in ${file}`)
}
function hasMetricHeaders(headers: Set<string>) {
return ["sumtokens", "sumtokensinput", "inputtokens", "totaltokens", "avgduration", "countdistinctsession"].some(
(header) => headers.has(header),
)
}
function hasHeader(headers: Set<string>, names: string[]) {
return names.some((name) => headers.has(normalizeHeader(name)))
}
function mergeFiles(left: Partial<Record<ImportKey, string[]>>, right: Partial<Record<ImportKey, string[]>>) {
return inputKeys.reduce<Partial<Record<ImportKey, string[]>>>((result, key) => {
const files = [...(left[key] ?? []), ...(right[key] ?? [])]
if (files.length === 0) return result
return { ...result, [key]: files }
}, {})
}
function modelProviderModelLookup(row: RawRow, grain: Grain, opts: ImportOptions): [string, string][] {
const base = basePeriod(row, grain, opts)
const value = providerModel(row)
const author = provider(row)
if (!value || !author) return []
return [[lookupKey({ ...base, dataset: opts.dataset, tier: tier(row), grain }, author, model(row)), value]]
}
function modelAggregate(
row: RawRow,
base: StatBaseAggregate,
providerModelLookup: Map<string, string>,
): ModelAggregate[] {
const author = provider(row)
if (!author) return []
return [
{
...base,
provider: author,
model: model(row),
provider_model: providerModelLookup.get(lookupKey(base, author, model(row))) ?? providerModel(row),
},
]
}
function geoContinentLookup(row: RawRow, grain: Grain, opts: ImportOptions): [string, string][] {
const base = basePeriod(row, grain, opts)
const value = continent(row)
if (!value) return []
return [[lookupKey({ ...base, dataset: opts.dataset, tier: tier(row), grain }, country(row)), value]]
}
function geoModelAggregate(row: RawRow, base: StatBaseAggregate, continentLookup: Map<string, string>): GeoAggregate[] {
const author = provider(row)
if (!author) return []
return [
{
...base,
provider: author,
model: model(row),
country: country(row),
continent: continentLookup.get(lookupKey(base, country(row))) ?? continent(row),
},
]
}
function baseAggregate(row: RawRow, grain: Grain, opts: ImportOptions): StatBaseAggregate {
return {
...basePeriod(row, grain, opts),
grain,
dataset: opts.dataset,
tier: tier(row),
sessions: integer(row, "sessions", ["COUNT_DISTINCT(session)"]),
requests: integer(row, "requests", ["COUNT", "COUNT()"]),
input_tokens: integer(row, "input_tokens", ["SUM(tokens.input)", "SUM(tokens_input)"]),
output_tokens: integer(row, "output_tokens", ["SUM(tokens.output)", "SUM(tokens_output)"]),
reasoning_tokens: integer(row, "reasoning_tokens", ["SUM(tokens.reasoning)", "SUM(tokens_reasoning)"]),
cache_read_tokens: integer(row, "cache_read_tokens", ["SUM(tokens.cache_read)", "SUM(tokens_cache_read)"]),
total_tokens: integer(row, "total_tokens", ["SUM(stat_tokens_total)", "SUM(tokens)", "SUM(tokens_total)"]),
input_cost_microcents: integer(row, "input_cost_microcents", [
"SUM(cost.input.microcents)",
"SUM(stat_cost_input_microcents)",
]),
output_cost_microcents: integer(row, "output_cost_microcents", [
"SUM(cost.output.microcents)",
"SUM(stat_cost_output_microcents)",
]),
total_cost_microcents: integer(row, "total_cost_microcents", [
"SUM(cost.total.microcents)",
"SUM(stat_cost_total_microcents)",
]),
avg_duration_ms: nullableNumber(row, "avg_duration_ms", ["AVG(duration)", "AVG(duration_ms)"]),
p50_duration_ms: nullableInteger(row, "p50_duration_ms", ["P50(duration)", "P50(duration_ms)"]),
p95_duration_ms: nullableInteger(row, "p95_duration_ms", ["P95(duration)", "P95(duration_ms)"]),
avg_ttfb_ms: nullableNumber(row, "avg_ttfb_ms", ["AVG(time_to_first_byte)", "AVG(ttfb_ms)"]),
p50_ttfb_ms: nullableInteger(row, "p50_ttfb_ms", ["P50(time_to_first_byte)", "P50(ttfb_ms)"]),
p95_ttfb_ms: nullableInteger(row, "p95_ttfb_ms", ["P95(time_to_first_byte)", "P95(ttfb_ms)"]),
avg_output_tps: nullableNumber(row, "avg_output_tps", ["AVG(tps.output)", "AVG(stat_output_tps)"]),
success_count: integer(row, "success_count", ["SUM(success)", "SUM(is_success)", "SUM(stat_success)"]),
error_count: integer(row, "error_count", ["SUM(error)", "SUM(is_error)", "SUM(stat_error)"]),
sample_count: integer(row, "sample_count", ["COUNT", "COUNT()"]),
}
}
function basePeriod(row: RawRow, grain: Grain, opts: ImportOptions) {
return { period_key: periodKey(row, grain, opts) }
}
function periodKey(row: RawRow, grain: Grain, opts: ImportOptions) {
if (grain === "week") {
const week = parseWeek(row)
if (week) return week
fail("weekly imports require a week or period_key column")
}
const time = parseTime(row)
const start = time ? startOfUtcDay(time) : opts.periodStart
if (!start) fail("daily imports require a time column or --period-start")
return periodKeyFor("day", start)
}
function modelRowsFromAggregates(aggregates: ModelAggregate[]) {
return rankModelRows([
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "week").map(toModelRow), modelDimensionKey),
modelDimensionKey,
),
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "day").map(toModelRow), modelDimensionKey),
modelDimensionKey,
),
])
}
function providerRowsFromAggregates(aggregates: ProviderAggregate[]) {
return rankRowsWithMarketShare([
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "week").map(toProviderRow), providerDimensionKey),
providerDimensionKey,
),
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "day").map(toProviderRow), providerDimensionKey),
providerDimensionKey,
),
])
}
function geoRowsFromAggregates(aggregates: GeoAggregate[]) {
return rankRowsWithMarketShare(
[
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "week").map(toGeoRow), geoDimensionKey),
geoDimensionKey,
),
...synthesizeAllTierRows(
collapseRows(aggregates.filter((item) => item.grain === "day").map(toGeoRow), geoDimensionKey),
geoDimensionKey,
),
],
geoMarketShareKey,
)
}
function toModelRow(data: ModelAggregate): ModelStatRow {
return { ...toStatBaseRow(data), provider: data.provider, model: data.model, provider_model: data.provider_model }
}
function toProviderRow(data: ProviderAggregate): ProviderStatRow {
return { ...toStatBaseRow(data), provider: data.provider }
}
function toGeoRow(data: GeoAggregate): GeoStatRow {
return {
...toStatBaseRow(data),
provider: data.provider,
model: data.model,
country: data.country,
continent: data.continent,
}
}
function rankModelRows(rows: ModelStatRow[]) {
return Object.values(
rows.reduce<Record<string, ModelStatRow[]>>((result, row) => {
const key = statPeriodKey(row)
result[key] = [...(result[key] ?? []), row]
return result
}, {}),
).flatMap((group) => {
const tokenRanks = rankBy(group, (row) => row.total_tokens ?? 0)
const requestRanks = rankBy(group, (row) => row.requests ?? 0)
const costRanks = rankBy(group, (row) => row.total_cost_microcents ?? 0)
return group.map((row) => ({
...row,
rank_by_tokens: tokenRanks.get(row) ?? null,
rank_by_requests: requestRanks.get(row) ?? null,
rank_by_cost: costRanks.get(row) ?? null,
}))
})
}
function modelDimensionKey(row: ModelStatRow) {
return [row.provider, row.model].join("\u0000")
}
function providerDimensionKey(row: ProviderStatRow) {
return row.provider
}
function geoDimensionKey(row: GeoStatRow) {
return [row.provider, row.model, row.country].join("\u0000")
}
function geoMarketShareKey(row: GeoStatRow) {
return [statPeriodKey(row), row.provider, row.model].join("\u0000")
}
function lookupKey(base: { grain: string; period_key: string; dataset: string; tier: string }, ...dimension: string[]) {
return [base.grain, base.period_key, base.dataset, base.tier, ...dimension].join("\u0000")
}
function tier(row: RawRow) {
return normalizeTier(cell(row, ["stat_tier", "tier"]) || deriveTier(row))
}
function deriveTier(row: RawRow) {
const source = cell(row, ["source"])
const value = model(row)
if (source === "lite") return "Go"
if (FREE_MODELS.has(value) || /-free(:global)?$/.test(rawModel(row))) return "Free"
return "Zen"
}
function provider(row: RawRow) {
return statProvider(model(row), providerModel(row), cell(row, ["stat_provider_2", "stat_provider"]))
}
function model(row: RawRow) {
return statModel(cell(row, ["stat_model_2", "stat_model"]) || rawModel(row), providerModel(row))
}
function rawModel(row: RawRow) {
return cell(row, ["model"]) || "unknown"
}
function providerModel(row: RawRow) {
return cell(row, ["provider.model", "provider_model"]) || ""
}
function country(row: RawRow) {
return normalizeCountry(cell(row, ["stat_country", "cf.country", "cf_country", "country"]))
}
function continent(row: RawRow) {
return cell(row, ["cf.continent", "cf_continent", "continent"]) || ""
}
function integer(row: RawRow, name: string, aliases: string[] = []) {
return Math.round(number(row, name, aliases))
}
function nullableInteger(row: RawRow, name: string, aliases: string[] = []) {
if (!hasCell(row, [name, ...aliases])) return null
return Math.round(number(row, name, aliases))
}
function nullableNumber(row: RawRow, name: string, aliases: string[] = []) {
if (!hasCell(row, [name, ...aliases])) return null
return Number(number(row, name, aliases).toFixed(2))
}
function number(row: RawRow, name: string, aliases: string[] = []) {
const value = Number(cell(row, [name, ...aliases]).replace(/,/g, ""))
return Number.isFinite(value) ? value : 0
}
function hasCell(row: RawRow, names: string[]) {
return names.some((name) => row[name] !== undefined && row[name] !== "")
}
function cell(row: RawRow, names: string[]) {
const normalized = normalizedCells(row)
return (
names.flatMap((name) => [row[name], normalized.get(normalizeHeader(name))]).find((value) => value !== undefined) ??
""
)
}
function normalizedCells(row: RawRow) {
return new Map(Object.entries(row).map(([key, value]) => [normalizeHeader(key), value]))
}
function normalizeHeader(value: string) {
return value.toLowerCase().replace(/[^a-z0-9]+/g, "")
}
function parseTime(row: RawRow) {
const value = cell(row, ["date", "time", "timestamp", "datetime", "bucket"])
if (!value) return undefined
const numeric = Number(value)
const date = Number.isFinite(numeric)
? new Date(numeric > 10_000_000_000 ? numeric : numeric * 1000)
: new Date(value)
if (Number.isNaN(date.getTime())) fail(`Invalid time value: ${value}`)
return date
}
function parseWeek(row: RawRow) {
const value = cell(row, ["period_key", "week", "stat_week"])
if (!value) return undefined
const match = /^(\d{4})-W(\d{1,2})$/.exec(value)
if (!match) fail(`Invalid week value: ${value}`)
const year = Number(match[1])
const week = Number(match[2])
if (week < 1 || week > 53) fail(`Invalid week value: ${value}`)
const start = new Date(startOfIsoWeek(new Date(Date.UTC(year, 0, 4))).getTime() + (week - 1) * 7 * DAY_MS)
const id = `${year}-W${String(week).padStart(2, "0")}`
if (isoWeekId(start) !== id) fail(`Invalid week value: ${value}`)
return id
}
async function readRows(file: string) {
const text = await Bun.file(file).text()
if (file.toLowerCase().endsWith(".json")) {
const parsed: unknown = JSON.parse(text)
return rowsFromJson(parsed)
}
return rowsFromCsv(text)
}
function rowsFromJson(value: unknown): RawRow[] {
if (Array.isArray(value)) return value.flatMap(rowFromUnknown)
if (!isRecord(value)) fail("JSON imports must be an array of rows or an object with results/data/rows")
const rows = [value.results, value.data, value.rows].flatMap((candidate) =>
Array.isArray(candidate) ? candidate.flatMap(rowFromUnknown) : [],
)
if (rows.length === 0) fail("JSON import did not contain rows")
return rows
}
function rowFromUnknown(value: unknown): RawRow[] {
if (!isRecord(value)) return []
const nested = isRecord(value.data) ? value.data : {}
return [
Object.fromEntries(
Object.entries({ ...value, ...nested }).flatMap(([key, item]) => {
if (key === "data") return []
return [[key, cellValue(item)]]
}),
),
]
}
function rowsFromCsv(text: string): RawRow[] {
const [headers, ...rows] = csvRecords(text).filter((row) => row.some((value) => value.trim() !== ""))
if (!headers) return []
return rows.map((row) =>
Object.fromEntries(headers.map((header, index) => [header.trim(), row[index]?.trim() ?? ""])),
)
}
function csvRecords(text: string) {
const rows: string[][] = []
let row: string[] = []
let field = ""
let quoted = false
for (let index = 0; index < text.length; index++) {
const char = text[index]
const next = text[index + 1]
if (quoted) {
if (char === '"' && next === '"') {
field += '"'
index++
continue
}
if (char === '"') {
quoted = false
continue
}
field += char
continue
}
if (char === '"') {
quoted = true
continue
}
if (char === ",") {
row.push(field)
field = ""
continue
}
if (char === "\n") {
row.push(field)
rows.push(row)
row = []
field = ""
continue
}
if (char === "\r") continue
field += char
}
row.push(field)
rows.push(row)
return rows
}
function cellValue(value: unknown) {
if (value === null || value === undefined) return ""
if (typeof value === "string") return value
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value)
return JSON.stringify(value) ?? ""
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
}
async function upsertModelRows(db: ReturnType<typeof drizzle>, rows: ModelStatRow[], chunkSize: number) {
const batches = chunks(rows, chunkSize)
console.log(JSON.stringify({ table: "model_stat", batches: batches.length, chunkSize }))
for (const chunk of batches) {
await db
.insert(modelStat)
.values(chunk)
.onDuplicateKeyUpdate({
set: {
provider_model: inserted("provider_model"),
sessions: inserted("sessions"),
requests: inserted("requests"),
input_tokens: inserted("input_tokens"),
output_tokens: inserted("output_tokens"),
reasoning_tokens: inserted("reasoning_tokens"),
cache_read_tokens: inserted("cache_read_tokens"),
total_tokens: inserted("total_tokens"),
input_cost_microcents: inserted("input_cost_microcents"),
output_cost_microcents: inserted("output_cost_microcents"),
total_cost_microcents: inserted("total_cost_microcents"),
avg_duration_ms: inserted("avg_duration_ms"),
p50_duration_ms: inserted("p50_duration_ms"),
p95_duration_ms: inserted("p95_duration_ms"),
avg_ttfb_ms: inserted("avg_ttfb_ms"),
p50_ttfb_ms: inserted("p50_ttfb_ms"),
p95_ttfb_ms: inserted("p95_ttfb_ms"),
avg_output_tps: inserted("avg_output_tps"),
success_count: inserted("success_count"),
error_count: inserted("error_count"),
sample_count: inserted("sample_count"),
rank_by_tokens: inserted("rank_by_tokens"),
rank_by_requests: inserted("rank_by_requests"),
rank_by_cost: inserted("rank_by_cost"),
},
})
}
}
async function upsertProviderRows(db: ReturnType<typeof drizzle>, rows: ProviderStatRow[], chunkSize: number) {
const batches = chunks(rows, chunkSize)
console.log(JSON.stringify({ table: "provider_stat", batches: batches.length, chunkSize }))
for (const chunk of batches) {
await db
.insert(providerStat)
.values(chunk)
.onDuplicateKeyUpdate({
set: {
sessions: inserted("sessions"),
requests: inserted("requests"),
input_tokens: inserted("input_tokens"),
output_tokens: inserted("output_tokens"),
reasoning_tokens: inserted("reasoning_tokens"),
cache_read_tokens: inserted("cache_read_tokens"),
total_tokens: inserted("total_tokens"),
input_cost_microcents: inserted("input_cost_microcents"),
output_cost_microcents: inserted("output_cost_microcents"),
total_cost_microcents: inserted("total_cost_microcents"),
avg_duration_ms: inserted("avg_duration_ms"),
p50_duration_ms: inserted("p50_duration_ms"),
p95_duration_ms: inserted("p95_duration_ms"),
avg_ttfb_ms: inserted("avg_ttfb_ms"),
p50_ttfb_ms: inserted("p50_ttfb_ms"),
p95_ttfb_ms: inserted("p95_ttfb_ms"),
avg_output_tps: inserted("avg_output_tps"),
success_count: inserted("success_count"),
error_count: inserted("error_count"),
sample_count: inserted("sample_count"),
market_share_tokens: inserted("market_share_tokens"),
market_share_requests: inserted("market_share_requests"),
market_share_sessions: inserted("market_share_sessions"),
rank_by_tokens: inserted("rank_by_tokens"),
rank_by_requests: inserted("rank_by_requests"),
rank_by_sessions: inserted("rank_by_sessions"),
rank_by_cost: inserted("rank_by_cost"),
},
})
}
}
async function upsertGeoRows(db: ReturnType<typeof drizzle>, rows: GeoStatRow[], chunkSize: number) {
const batches = chunks(rows, chunkSize)
console.log(JSON.stringify({ table: "geo_stat", batches: batches.length, chunkSize }))
for (const chunk of batches) {
await db
.insert(geoStat)
.values(chunk)
.onDuplicateKeyUpdate({
set: {
continent: inserted("continent"),
sessions: inserted("sessions"),
requests: inserted("requests"),
input_tokens: inserted("input_tokens"),
output_tokens: inserted("output_tokens"),
reasoning_tokens: inserted("reasoning_tokens"),
cache_read_tokens: inserted("cache_read_tokens"),
total_tokens: inserted("total_tokens"),
input_cost_microcents: inserted("input_cost_microcents"),
output_cost_microcents: inserted("output_cost_microcents"),
total_cost_microcents: inserted("total_cost_microcents"),
avg_duration_ms: inserted("avg_duration_ms"),
p50_duration_ms: inserted("p50_duration_ms"),
p95_duration_ms: inserted("p95_duration_ms"),
avg_ttfb_ms: inserted("avg_ttfb_ms"),
p50_ttfb_ms: inserted("p50_ttfb_ms"),
p95_ttfb_ms: inserted("p95_ttfb_ms"),
avg_output_tps: inserted("avg_output_tps"),
success_count: inserted("success_count"),
error_count: inserted("error_count"),
sample_count: inserted("sample_count"),
market_share_tokens: inserted("market_share_tokens"),
market_share_requests: inserted("market_share_requests"),
market_share_sessions: inserted("market_share_sessions"),
rank_by_tokens: inserted("rank_by_tokens"),
rank_by_requests: inserted("rank_by_requests"),
rank_by_sessions: inserted("rank_by_sessions"),
rank_by_cost: inserted("rank_by_cost"),
},
})
}
}
function parseImportOptions(args: string[]): ImportOptions {
const flags = parseFlags(args)
const files = inputKeys.reduce<Partial<Record<ImportKey, string[]>>>((result, key) => {
const values = flags.get(key)
if (!values) return result
return { ...result, [key]: values }
}, {})
return {
dataset: flags.get("dataset")?.[0] ?? "zen",
databaseUrl: flags.get("database-url")?.[0] ?? process.env.DATABASE_URL,
directories: flags.get("dir") ?? flags.get("directory") ?? [],
dryRun: flags.has("dry-run"),
periodStart: parseDateFlag(flags, "period-start"),
upsertChunkSize: parseIntegerFlag(flags, "upsert-chunk-size") ?? DEFAULT_UPSERT_CHUNK_SIZE,
files,
}
}
function parseFlags(args: string[]) {
const result = new Map<string, string[]>()
for (let index = 0; index < args.length; index++) {
const arg = args[index]
if (!arg.startsWith("--")) fail(`Unexpected argument: ${arg}`)
const name = arg.slice(2)
if (name === "dry-run" || name === "include-weekly") {
result.set(name, ["true"])
continue
}
const nextFlag = args.findIndex((value, valueIndex) => valueIndex > index && value.startsWith("--"))
const values = args.slice(index + 1, nextFlag === -1 ? args.length : nextFlag)
if (values.length === 0) fail(`Missing value for --${name}`)
result.set(name, [...(result.get(name) ?? []), ...values])
index += values.length
}
return result
}
function parseDateFlag(flags: Map<string, string[]>, name: string) {
const value = flags.get(name)?.[0]
if (!value) return undefined
const date = new Date(value)
if (Number.isNaN(date.getTime())) fail(`Invalid --${name}: ${value}`)
return date
}
function parseIntegerFlag(flags: Map<string, string[]>, name: string) {
const value = flags.get(name)?.[0]
if (!value) return undefined
const parsed = Number(value)
if (!Number.isInteger(parsed) || parsed <= 0) fail(`Invalid --${name}: ${value}`)
return parsed
}
function parseListFlag(flags: Map<string, string[]>, name: string) {
const value = flags.get(name)?.[0]
if (!value) return undefined
if (value === "all") return ["all"]
return value
.split(",")
.map((item) => item.trim())
.filter(Boolean)
}
function usage(): never {
fail(`Usage:
bun src/honeycomb-backfill.ts queries [--tiers Go,Free,Paid] [--limit 1000]
bun src/honeycomb-backfill.ts import [--dry-run] [--upsert-chunk-size 100] [--database-url URL] --dir downloads
bun src/honeycomb-backfill.ts import [--dry-run] [--upsert-chunk-size 100] [--database-url URL] --model-day file.csv [--model-day more.csv] ...`)
}
function fail(message: string): never {
console.error(message)
process.exit(1)
}

View File

@@ -0,0 +1,11 @@
export * as Athena from "./athena"
export * as AppConfig from "./config"
export * as Database from "./database"
export * as GeoStat from "./domain/geo"
export * as StatsHome from "./domain/home"
export * as Inference from "./domain/inference"
export * as ModelStat from "./domain/model"
export * as ProviderStat from "./domain/provider"
export * as Stat from "./domain/stat"
export * as Runtime from "./runtime"
export * as StatSync from "./stat-sync"

View File

@@ -0,0 +1,4 @@
import { Effect } from "effect"
import { layer, migrate } from "./database"
await Effect.runPromise(migrate().pipe(Effect.provide(layer)))

28
packages/stats/core/src/resource.d.ts vendored Normal file
View File

@@ -0,0 +1,28 @@
import "sst/resource"
declare module "sst/resource" {
export interface Resource {
InferenceEvent: {
catalog: string
database: string
region: string
table: string
tableBucket: string
type: "sst.sst.Linkable"
workgroup: string
}
StatsSyncConfig: {
dataset: string
type: "sst.sst.Linkable"
}
StatsDatabase: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
url: string
username: string
}
}
}

View File

@@ -0,0 +1,14 @@
import { Layer, ManagedRuntime } from "effect"
import { AppConfig } from "./config"
import { layer as databaseLayer } from "./database"
import { GeoStatRepo } from "./domain/geo"
import { ModelStatRepo } from "./domain/model"
import { ProviderStatRepo } from "./domain/provider"
const repoLayer = Layer.mergeAll(ModelStatRepo.layer, ProviderStatRepo.layer, GeoStatRepo.layer).pipe(
Layer.provide(databaseLayer),
)
export const layer = Layer.mergeAll(AppConfig.layer, databaseLayer, repoLayer)
export const runtime = ManagedRuntime.make(layer)
export type RuntimeServices = ManagedRuntime.ManagedRuntime.Services<typeof runtime>

View File

@@ -0,0 +1,101 @@
import { DateTime, Effect } from "effect"
import { Resource } from "sst/resource"
import { Athena, AthenaQueryError, AthenaQueryTimeoutError } from "./athena"
import { DatabaseError } from "./database"
import { GeoStatRepo, rowsFromAggregates as geoRowsFromAggregates } from "./domain/geo"
import { buildStatsQuery, toGeoAggregate, toModelAggregate, toProviderAggregate } from "./domain/inference"
import { ModelStatRepo, rowsFromAggregates as modelRowsFromAggregates } from "./domain/model"
import { ProviderStatRepo, rowsFromAggregates as providerRowsFromAggregates } from "./domain/provider"
import { startOfIsoWeek } from "./domain/stat"
const DATALAKE_INGESTION_LAG_MS = 5 * 60_000
const STATS_DATA_START_MS = new Date("2026-05-28T00:00:00.000Z").getTime()
const WEEK_MS = 7 * 86_400_000
export type SyncStatsResult = { ok: true; rows: number; startedAt: string; periodStart: string; periodEnd: string }
export type SyncStatsError = AthenaQueryError | AthenaQueryTimeoutError | DatabaseError
export const syncStats: () => Effect.Effect<
SyncStatsResult,
SyncStatsError,
Athena | ModelStatRepo | ProviderStatRepo | GeoStatRepo
> = Effect.fn("StatSync.sync")(function* () {
const startedAt = yield* DateTime.nowAsDate
const periodEnd = new Date(Math.floor((startedAt.getTime() - DATALAKE_INGESTION_LAG_MS) / 60_000) * 60_000)
// May 27 was partial, so keep Athena stats anchored at the first complete day.
const periodStart = new Date(Math.max(startOfIsoWeek(periodEnd).getTime() - WEEK_MS, STATS_DATA_START_MS))
const athena = yield* Athena
const modelStats = yield* ModelStatRepo
const providerStats = yield* ProviderStatRepo
const geoStats = yield* GeoStatRepo
yield* logRuntimeCheck()
const [modelAggregates, providerAggregates, geoAggregates, geoModelAggregates] = yield* Effect.all(
[
athena
.query(buildStatsQuery(periodStart, periodEnd, "model"))
.pipe(Effect.map((rows) => rows.flatMap(toModelAggregate))),
athena
.query(buildStatsQuery(periodStart, periodEnd, "provider"))
.pipe(Effect.map((rows) => rows.flatMap(toProviderAggregate))),
athena
.query(buildStatsQuery(periodStart, periodEnd, "geo"))
.pipe(Effect.map((rows) => rows.flatMap(toGeoAggregate))),
athena
.query(buildStatsQuery(periodStart, periodEnd, "geo_model"))
.pipe(Effect.map((rows) => rows.flatMap(toGeoAggregate))),
],
{ concurrency: "unbounded" },
)
const modelRows = modelRowsFromAggregates(modelAggregates)
const providerRows = providerRowsFromAggregates(providerAggregates)
const geoRows = geoRowsFromAggregates([...geoAggregates, ...geoModelAggregates])
yield* Effect.all([modelStats.upsert(modelRows), providerStats.upsert(providerRows), geoStats.upsert(geoRows)], {
concurrency: "unbounded",
discard: true,
})
yield* Effect.all(
[
modelStats.deleteRetiredDimensions(modelRows),
providerStats.deleteRetiredDimensions(providerRows),
geoStats.deleteRetiredDimensions(geoRows),
],
{ concurrency: "unbounded", discard: true },
)
yield* Effect.logInfo(
`stats sync complete ${JSON.stringify({
startedAt: startedAt.toISOString(),
periodStart: periodStart.toISOString(),
periodEnd: periodEnd.toISOString(),
rows: modelRows.length,
providerRows: providerRows.length,
geoRows: geoRows.length,
stage: Resource.App.stage,
})}`,
)
return {
ok: true,
rows: modelRows.length,
startedAt: startedAt.toISOString(),
periodStart: periodStart.toISOString(),
periodEnd: periodEnd.toISOString(),
}
})
function logRuntimeCheck() {
return Effect.logInfo(
`athena stats runtime check ${JSON.stringify({
catalog: Resource.InferenceEvent.catalog,
database: Resource.InferenceEvent.database,
dataset: Resource.StatsSyncConfig.dataset,
table: Resource.InferenceEvent.table,
workgroup: Resource.InferenceEvent.workgroup,
region: Resource.InferenceEvent.region,
stage: Resource.App.stage,
})}`,
)
}

10
packages/stats/core/sst-env.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}

View File

@@ -0,0 +1,11 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node22/tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"types": ["bun", "node"]
}
}

View File

@@ -0,0 +1,32 @@
FROM oven/bun:1.3.14-alpine AS base
WORKDIR /app
ENV NODE_ENV=production
ENV BUN_RUNTIME_TRANSPILER_CACHE_PATH=0
FROM base AS pruner
COPY . .
RUN bunx turbo@2.8.13 prune @opencode-ai/stats-server --docker --no-update-notifier --no-color
FROM base AS installer
COPY --from=pruner /app/out/json/ ./
# Bun 1.3.x needs the pruned workspace globs and lockfile metadata refreshed before the frozen production install.
RUN bun -e 'const packageJson = await Bun.file("package.json").json(); packageJson.workspaces.packages = Array.from(new Bun.Glob("packages/**/package.json").scanSync(".")).map((file) => file.slice(0, -"/package.json".length)).sort(); await Bun.write("package.json", JSON.stringify(packageJson, null, 2) + "\n")'
RUN rm -f bun.lock && bun install --filter @opencode-ai/stats-server --lockfile-only --ignore-scripts
RUN bun install --filter @opencode-ai/stats-server --frozen-lockfile --production --ignore-scripts
FROM base AS runner
COPY --from=installer /app ./
COPY --from=pruner /app/out/full/ ./
WORKDIR /app/packages/stats/server
EXPOSE 3000
CMD ["bun", "src/server.ts"]

View File

@@ -0,0 +1,33 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-server",
"version": "1.17.4",
"private": true,
"type": "module",
"license": "MIT",
"main": "./src/server.ts",
"exports": {
".": "./src/server.ts"
},
"scripts": {
"start": "bun src/server.ts",
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"@aws-sdk/client-firehose": "3.933.0",
"@effect/platform-node": "catalog:",
"@opencode-ai/stats-core": "workspace:*",
"effect": "catalog:",
"sst": "catalog:"
},
"devDependencies": {
"@tsconfig/node22": "catalog:",
"@types/bun": "catalog:",
"@types/node": "catalog:",
"@typescript/native-preview": "catalog:",
"typescript": "catalog:"
},
"engines": {
"node": ">=22"
}
}

View File

@@ -0,0 +1,159 @@
import { Buffer } from "node:buffer"
import { FirehoseClient, PutRecordBatchCommand } from "@aws-sdk/client-firehose"
import { Effect, Layer, Schema } from "effect"
import * as Context from "effect/Context"
import { Resource } from "sst/resource"
const MAX_FIREHOSE_BATCH_SIZE = 500
const MAX_FIREHOSE_ATTEMPTS = 3
const LAKE_TYPE = /^([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)$/
type IngestEvent = Record<string, unknown>
type LakeRoute = { database: string; table: string }
type FirehoseRecord = { Data: Uint8Array }
export class IngestError extends Schema.TaggedErrorClass<IngestError>()("IngestError", {
message: Schema.String,
failed: Schema.Number,
cause: Schema.optional(Schema.Defect),
}) {}
export declare namespace Ingest {
export interface Service {
readonly write: (events: unknown[]) => Effect.Effect<{ records: number }, IngestError>
}
}
export class Ingest extends Context.Service<Ingest, Ingest.Service>()("@opencode/stats/Ingest") {
static readonly layer: Layer.Layer<Ingest> = Layer.effect(
Ingest,
Effect.sync(() => {
const client = new FirehoseClient({})
const write = Effect.fn("Ingest.write")(function* (events: unknown[]) {
if (events.length === 0) return { records: 0 }
const counts = countRoutedEvents(events)
if (counts.unsupported > 0) {
yield* Effect.logWarning(
`lake ingest rejected ${JSON.stringify({ records: counts.records, unsupported: counts.unsupported })}`,
)
return yield* new IngestError({
message: "Unsupported lake event type",
failed: counts.unsupported,
})
}
if (counts.records === 0) return { records: 0 }
let batch: FirehoseRecord[] = []
let batches = 0
let failed = 0
for (const event of events) {
if (!isRecord(event)) continue
const route = routeEvent(event)
if (!route) continue
batch.push(toFirehoseRecord(event, route))
if (batch.length < MAX_FIREHOSE_BATCH_SIZE) continue
failed += yield* putRecords(client, Resource.LakeIngestConfig.streamName, batch)
batches++
batch = []
}
if (batch.length > 0) {
failed += yield* putRecords(client, Resource.LakeIngestConfig.streamName, batch)
batches++
}
if (failed > 0) {
yield* Effect.logWarning(`lake ingest incomplete ${JSON.stringify({ records: counts.records, failed })}`)
return yield* new IngestError({ message: "Failed to ingest all lake records", failed })
}
yield* Effect.logInfo(`lake ingest complete ${JSON.stringify({ records: counts.records, batches })}`)
return { records: counts.records }
})
return Ingest.of({ write })
}),
)
}
const putRecords: (
client: FirehoseClient,
streamName: string,
records: FirehoseRecord[],
attempt?: number,
) => Effect.Effect<number, IngestError> = Effect.fn("Ingest.putRecords")(function* (
client,
streamName,
records,
attempt = 1,
) {
const result = yield* Effect.tryPromise({
try: () => client.send(new PutRecordBatchCommand({ DeliveryStreamName: streamName, Records: records })),
catch: (cause) =>
new IngestError({ message: "Failed to write lake records to Firehose", failed: records.length, cause }),
}).pipe(
Effect.tapError(() =>
Effect.logWarning(`firehose batch write failed ${JSON.stringify({ records: records.length, attempt })}`),
),
)
const failed =
result.RequestResponses?.flatMap((item, index) => {
const record = records[index]
if (!item.ErrorCode || !record) return []
return [record]
}) ?? []
if (failed.length === 0) return 0
if (attempt >= MAX_FIREHOSE_ATTEMPTS) {
yield* Effect.logWarning(
`firehose batch failed ${JSON.stringify({ records: failed.length, attempts: MAX_FIREHOSE_ATTEMPTS })}`,
)
return failed.length
}
yield* Effect.logWarning(
`firehose batch retrying ${JSON.stringify({ records: failed.length, attempt: attempt + 1 })}`,
)
yield* Effect.sleep(`${250 * 2 ** (attempt - 1)} millis`)
return yield* putRecords(client, streamName, failed, attempt + 1)
})
function countRoutedEvents(events: unknown[]) {
let records = 0
let unsupported = 0
for (const event of events) {
if (!isRecord(event)) continue
if (routeEvent(event)) records++
else unsupported++
}
return { records, unsupported }
}
function isRecord(item: unknown): item is IngestEvent {
return Boolean(item) && typeof item === "object" && !Array.isArray(item)
}
function routeEvent(event: IngestEvent): LakeRoute | undefined {
if (typeof event._datalake_key !== "string") return
const match = event._datalake_key.match(LAKE_TYPE)
if (!match?.[1] || !match[2]) return
return {
database: match[1],
table: match[2],
}
}
function toFirehoseRecord(event: IngestEvent, route: LakeRoute): FirehoseRecord {
return {
Data: Buffer.from(
JSON.stringify({
...Object.fromEntries(Object.entries(event).filter(([key]) => key !== "_datalake_key")),
_lake_database: route.database,
_lake_table: route.table,
_lake_operation: "insert" as const,
}),
),
}
}

11
packages/stats/server/src/resource.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
import "sst/resource"
declare module "sst/resource" {
export interface Resource {
LakeIngestConfig: {
secret: string
streamName: string
type: "sst.sst.Linkable"
}
}
}

View File

@@ -0,0 +1,73 @@
import { Buffer } from "node:buffer"
import { timingSafeEqual } from "node:crypto"
import { Effect, Schema } from "effect"
import * as Semaphore from "effect/Semaphore"
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
import { Resource } from "sst/resource"
import { Ingest } from "./ingest"
import { isShuttingDown } from "./shutdown"
const MAX_CONCURRENT_INGEST_REQUESTS = 8
const IngestPayload = Schema.Struct({
events: Schema.optional(Schema.Unknown),
})
export const Routes = HttpRouter.use((router) =>
Effect.gen(function* () {
const ingestService = yield* Ingest
const ingestRequests = yield* Semaphore.make(MAX_CONCURRENT_INGEST_REQUESTS)
yield* Effect.all(
[
router.add("GET", "/health", () => json(200, { ok: true })),
router.add("GET", "/ready", () => json(isShuttingDown() ? 503 : 200, { ok: !isShuttingDown() })),
router.add("POST", "/", ingestRequests.withPermit(ingest(ingestService))),
],
{ discard: true },
)
}),
)
const ingest = (ingestService: Ingest.Service) =>
Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest
if (!isAuthorized(request.headers)) return yield* json(401, { ok: false, error: "Unauthorized" })
const payload = yield* HttpServerRequest.schemaBodyJson(IngestPayload).pipe(
Effect.match({
onFailure: () => undefined,
onSuccess: (value) => value,
}),
)
if (!payload) return yield* json(400, { ok: false, error: "Invalid JSON body" })
const events = Array.isArray(payload.events) ? payload.events : []
if (events.length === 0) return yield* json(202, { ok: true, records: 0 })
return yield* ingestService.write(events).pipe(
Effect.flatMap((result) => json(202, { ok: true, records: result.records })),
Effect.catchTag("IngestError", (error) =>
json(502, { ok: false, records: countRecords(events), failed: error.failed }),
),
)
})
function isAuthorized(headers: Record<string, string | undefined>) {
const actual = Buffer.from(headers.authorization ?? headers.Authorization ?? "")
const expected = Buffer.from(`Bearer ${Resource.LakeIngestConfig.secret}`)
if (actual.length !== expected.length) return false
return timingSafeEqual(actual, expected)
}
function countRecords(items: unknown[]) {
let records = 0
for (const item of items) {
if (Boolean(item) && typeof item === "object" && !Array.isArray(item)) records++
}
return records
}
function json(status: number, body: Record<string, unknown>) {
return HttpServerResponse.json(body, { status }).pipe(Effect.orDie)
}

View File

@@ -0,0 +1,28 @@
import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
import { Config, Layer } from "effect"
import { HttpRouter } from "effect/unstable/http"
import { createServer } from "node:http"
import { Ingest } from "./ingest"
import { Routes } from "./router"
import { registerShutdownSignalHandlers } from "./shutdown"
registerShutdownSignalHandlers()
const ServerLive = NodeHttpServer.layerConfig(
() => createServer(),
Config.all({
port: Config.number("PORT").pipe(Config.withDefault(3000)),
host: Config.string("HOST").pipe(Config.withDefault("0.0.0.0")),
}),
)
const runtimeLayer = Ingest.layer
const programLayer = Routes.pipe(Layer.provide(runtimeLayer))
const main = Layer.launch(
HttpRouter.serve(programLayer, {
disableLogger: true,
}).pipe(Layer.provideMerge(ServerLive)),
)
NodeRuntime.runMain(main, { disableErrorReporting: true })

View File

@@ -0,0 +1,17 @@
let shuttingDown = false
let signalHandlersRegistered = false
export function isShuttingDown() {
return shuttingDown
}
export function registerShutdownSignalHandlers() {
if (signalHandlersRegistered) return
signalHandlersRegistered = true
process.once("SIGTERM", markShuttingDown)
process.once("SIGINT", markShuttingDown)
}
function markShuttingDown() {
shuttingDown = true
}

View File

@@ -0,0 +1,22 @@
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
import { Athena } from "@opencode-ai/stats-core/athena"
import { layer as statsLayer } from "@opencode-ai/stats-core/runtime"
import { syncStats } from "@opencode-ai/stats-core/stat-sync"
import { Cause, Effect, Layer, Schedule } from "effect"
const SYNC_INTERVAL = "1 hour"
const runtimeLayer = Layer.mergeAll(statsLayer, Athena.layer)
const syncPass = syncStats().pipe(
Effect.catchCause((cause) =>
Effect.logWarning(`stats sync failed ${JSON.stringify({ cause: Cause.pretty(cause) })}`),
),
)
const daemon = Effect.logInfo("stats sync daemon started").pipe(
Effect.andThen(syncPass.pipe(Effect.repeat(Schedule.fixed(SYNC_INTERVAL)))),
Effect.forkScoped,
)
NodeRuntime.runMain(Layer.launch(Layer.effectDiscard(daemon).pipe(Layer.provide(runtimeLayer))), {
disableErrorReporting: true,
})

10
packages/stats/server/sst-env.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}

View File

@@ -0,0 +1,12 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node22/tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"types": ["bun", "node"]
},
"include": ["src", "../core/src/resource.d.ts"]
}