Summary
Today the canonical pricing data is git-committed JSON served as static files over the GitHub raw CDN (docs/database.md: "there is no separate backend"). Every consumer materializes the entire dataset to answer one question, which makes the dashboard and SDKs slower than they need to be:
- Dashboard (
services/dashboard/src/lib/data.ts) fetches the full ~2.9 MB prices.json on mount, parses it, and explorer.tsx filters the 3,244-row array on every keystroke (O(n) substring scan, no index). History charts fetch up to 12 × ~2.8 MB snapshots via the GitHub contents API.
- SDKs (
libraries/python, libraries/typescript) parse the full 2.9 MB blob on cold start (~100–200 ms), cache 6 h, then run search_models as an O(n) linear scan.
- Sync re-parses the full prior 2.9 MB snapshot to diff, and
database/history/ is ~115 MB (≤42 snapshots, pruned in CI).
The row count is small (3,244); the cost is payload + parse, not the pricing math (already trivial multiplication).
Decision (ADR 0001)
Keep JSON as the canonical, git-committed, diff-reviewable source of truth, and add a derived SQLite artifact (prices.db) built by the sync pipeline and published as a CI artifact (GitHub Pages + Releases) — not committed to git. Consumers gain a fast SQLite read path (HTTP range requests via sql.js-httpvfs, indexes, FTS5) with the JSON path retained as fallback.
📄 Full rationale, alternatives, and proposed v1 schema: wiki/decisions/0001-canonical-pricing-database-storage.md (draft PR below).
Why this is "way faster"
- Partial reads —
sql.js-httpvfs + HTTP Range fetch only the DB pages a query touches (tens of KB) instead of the whole 2.9 MB file.
- Indexes / FTS5 — per-keystroke
O(n) filter and search_models scan become index-backed.
- No cold-start parse — SDKs that ship
prices.db skip the full-JSON parse; SQLite memory-maps the file.
- History as a time series — one
price_history table replaces ~115 MB of snapshot files; charts run one indexed query.
Phased rollout
Phase 0 — Build & publish prices.db (non-breaking) 🔴 blocks the rest
Phase 1 — Dashboard reads SQLite (JSON fallback)
Phase 2 — Optional SQLite backend in the SDKs
Phase 3 — History & sync diffing on the DB
Phase 4 — Revisit JSON retention (follow-up decision record)
Acceptance criteria
- Dashboard cold-load payload drops from ~2.9 MB to tens of KB; search/filter is index-backed.
- SDK filtered queries are index-backed; existing public APIs and JSON URLs keep working.
prices.db is reproducible from JSON in CI and validated against it every sync.
- No binary committed to git history.
Open questions
REAL vs TEXT-decimal price storage (Phase 0).
- Is HTTP
Range reliable enough on the chosen origin for sql.js-httpvfs, or do we ship a whole-file .db per load?
- Publish target — GitHub Pages, Releases, or both?
- Long-term: keep JSON canonical (recommended) or eventually retire it (Phase 4 decision record)?
Tracked by decision record wiki/decisions/0001-canonical-pricing-database-storage.md. Draft PR introduces it and the index.
Summary
Today the canonical pricing data is git-committed JSON served as static files over the GitHub raw CDN (
docs/database.md: "there is no separate backend"). Every consumer materializes the entire dataset to answer one question, which makes the dashboard and SDKs slower than they need to be:services/dashboard/src/lib/data.ts) fetches the full ~2.9 MBprices.jsonon mount, parses it, andexplorer.tsxfilters the 3,244-row array on every keystroke (O(n)substring scan, no index). History charts fetch up to 12 × ~2.8 MB snapshots via the GitHub contents API.libraries/python,libraries/typescript) parse the full 2.9 MB blob on cold start (~100–200 ms), cache 6 h, then runsearch_modelsas anO(n)linear scan.database/history/is ~115 MB (≤42 snapshots, pruned in CI).The row count is small (3,244); the cost is payload + parse, not the pricing math (already trivial multiplication).
Decision (ADR 0001)
Keep JSON as the canonical, git-committed, diff-reviewable source of truth, and add a derived SQLite artifact (
prices.db) built by the sync pipeline and published as a CI artifact (GitHub Pages + Releases) — not committed to git. Consumers gain a fast SQLite read path (HTTP range requests viasql.js-httpvfs, indexes, FTS5) with the JSON path retained as fallback.📄 Full rationale, alternatives, and proposed v1 schema:
wiki/decisions/0001-canonical-pricing-database-storage.md(draft PR below).Why this is "way faster"
sql.js-httpvfs+ HTTPRangefetch only the DB pages a query touches (tens of KB) instead of the whole 2.9 MB file.O(n)filter andsearch_modelsscan become index-backed.prices.dbskip the full-JSON parse; SQLite memory-maps the file.price_historytable replaces ~115 MB of snapshot files; charts run one indexed query.Phased rollout
Phase 0 — Build & publish
prices.db(non-breaking) 🔴 blocks the restbuild-dbstep toservices/syncthat materializes JSON → SQLite using the proposed v1 schema (models,model_sources,providers,price_history,models_fts,meta+PRAGMA user_version).VACUUM+ANALYZEthe DB; emit a checksum.Rangesupport on GitHub Pages andraw.githubusercontent.com(spike). If unreliable, fall back to single-.db-download + in-browser query.prices.dbto GitHub Pages (alongside the dashboard) and attach to a Release indatabase-sync.yml. Do not commit the binary.services/sync/tests.REAL(matches current float semantics) vsTEXTdecimals.Phase 1 — Dashboard reads SQLite (JSON fallback)
sql.js-httpvfsinto the dashboard; querymodels_ftsfor search and indexed columns for filters.loadPricingData()JSON path as fallback when the DB / range requests are unavailable oruser_versionmismatches.Phase 2 — Optional SQLite backend in the SDKs
sqlite3(stdlib) read path; optional bundledprices.dbor download-and-cache. Keep HTTP-JSON default for ≥1 release.better-sqlite3(Node) /sql.js(browser) read path; same fallback policy.search_modelsand lookups go through indexes/FTS; preserve public API and currency-conversion behavior unchanged.Phase 3 — History & sync diffing on the DB
price_historyquery.database/history/*.jsononce the DB carries history.Phase 4 — Revisit JSON retention (follow-up decision record)
wiki/decisions/. Requires a reviewability answer (e.g. a JSON-export diff bot) before any retirement.Acceptance criteria
prices.dbis reproducible from JSON in CI and validated against it every sync.Open questions
REALvsTEXT-decimal price storage (Phase 0).Rangereliable enough on the chosen origin forsql.js-httpvfs, or do we ship a whole-file.dbper load?Tracked by decision record
wiki/decisions/0001-canonical-pricing-database-storage.md. Draft PR introduces it and the index.