Skip to content

fix: correct model lineup (live), remove legacy supported_models catalog, persist platform pricing#259

Open
Gajesh2007 wants to merge 2 commits into
masterfrom
fix/model-catalog-and-pricing-restart
Open

fix: correct model lineup (live), remove legacy supported_models catalog, persist platform pricing#259
Gajesh2007 wants to merge 2 commits into
masterfrom
fix/model-catalog-and-pricing-restart

Conversation

@Gajesh2007
Copy link
Copy Markdown
Member

@Gajesh2007 Gajesh2007 commented May 30, 2026

Summary

Resolves the stale landing-page model lineup (#257), removes its backend root
cause (a duplicate legacy catalog), and fixes a critical billing bug found
along the way where platform pricing was wiped on every coordinator restart.

The real, served lineup today is Gemma 4 26B + GPT-OSS 20B (verified against
live /v1/models/catalog). Everything now flows from the manifest-backed
model_registry as the single source of truth.


1. Critical fix — platform pricing wiped on every restart

coordinator/store/postgres.go ran this in its startup migration list on every
boot
:

DELETE FROM model_prices WHERE account_id NOT IN (SELECT account_id FROM users);

It was a one-time cleanup for legacy Solana-wallet-keyed rows, but platform
default pricing is stored under the synthetic account_id = 'platform', which is
never a row in users — so all per-model platform pricing was deleted on every
restart
, silently reverting billing (consumer.go, provider.go) to the
$0.05 / $0.20 fallback. Models survived because they live in a different table
(model_registry).

Fix:

  • Exclude the 'platform' system account from the cleanup.
  • Gate the one-time destructive cleanup behind a new schema_migrations marker
    so it runs once, not on every boot (defense against the next synthetic
    account hitting the same trap).

Tests: TestPostgresWalletPriceCleanupPreservesPlatform and
TestPostgresWalletPriceCleanupRunsOnce (verified against a real Postgres;
proven to fail on the old query and pass with the fix).

After deploy, re-set the two model prices once via PUT /v1/admin/pricing
they now persist across restarts.

2. Backend — remove the legacy supported_models catalog

supported_models was a duplicate of model_registry, read only by the bare
admin CRUD and as an empty-registry fallback. It's what made the lineup look
"5-strong". Removed:

  • Routes: bare GET/POST/DELETE /v1/admin/models + their handlers.
  • Store: SetSupportedModel / ListSupportedModels / DeleteSupportedModel
    (interface + memory + postgres) and the supported_models table (now
    DROP TABLE IF EXISTS).
  • Empty-registry fallbacks in activeCatalogLookups, SyncModelCatalog,
    handleModelCatalog (now registry-only).
  • model_catalog_filter.go (the IsRetiredProviderModel token hack) and the
    dead seedModelCatalog.
  • Repointed scripts/admin.sh models list to /v1/models/catalog.

Untouched / intact: the manifest-backed registry path
(POST /v1/admin/models/register, /v1/admin/models/{id}/...),
PUT /v1/admin/pricing, /v1/models*, and the store.SupportedModel struct
(now documented as the in-memory shape derived from the registry).

3. Frontend / docs — live lineup + stale removal

  • landing/index.html: the pricing table is now sourced live from
    /v1/models/catalog + /v1/pricing, with the two served models as a static
    fallback. The API example uses gemma-4-26b.
  • landing/earn-calculator.js: model list now built live from the catalog
    (mirrors the already-live console earn page), with a corrected 2-model
    fallback.
  • Public read endpoints (/v1/models/catalog, /v1/pricing) now return a
    wildcard CORS origin (no credentials) so the marketing site can read them
    cross-origin. Credentialed endpoints stay locked to the single configured
    origin. Covered by TestCORSPublicEndpointsAllowAnyOrigin.
  • README.md: model table, example model id, and pricing table corrected to the
    two served models; the 95% vs 100% revenue-split contradiction resolved to
    100% during the public alpha (consistent with the landing page and the 0%
    platform fee default).
  • console-ui (models, api/stats, api-console, providers/setup) and the
    scripts/*.py benchmark/load lists updated to the served lineup.

Out of scope (intentionally not changed)

  • papers/dginf-private-inference.tex — published benchmark content, not product
    surface.
  • Go test fixtures that use old model ids as arbitrary strings (harmless).
  • scripts/mlx_lm_batch_bench.py — uses the real HuggingFace weights path for a
    standalone MLX tool, not a coordinator model id.

Testing

  • go build ./..., go vet ./..., gofmt -l clean.
  • coordinator api + store (vs ephemeral Postgres) + cmd suites pass.
  • console-ui: npx eslint src/ (0 errors) and npm run build pass.
  • Landing scripts: client transform verified against live prod data; JS
    syntax-checked.

Note: TestPostgresRecordUsage is a pre-existing flake (asserts row order
on equal created_at timestamps); it fails on the pristine tree too and is
unrelated to this PR.

Deploy ordering

Ship the coordinator first — the landing page's live fetch only works in-browser
once the new CORS is deployed (until then it shows the correct static fallback).

Refs #257, #256


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

…n restart

Addresses the stale model lineup (#257) and its backend root cause, plus a
critical billing bug found along the way.

Pricing (critical): the model_prices startup cleanup deleted the synthetic
"platform" account rows on every boot (platform != a users row), silently
reverting billing to the fallback $0.05/$0.20. Exclude "platform" and gate the
one-time cleanup behind a schema_migrations marker so it never runs again.

Backend: remove the legacy supported_models catalog -- the bare
GET/POST/DELETE /v1/admin/models endpoints, the store CRUD + table (now
DROP TABLE), the empty-registry fallbacks, the IsRetiredProviderModel filter,
and the dead seedModelCatalog. The manifest-backed model_registry is the single
source of truth. The SupportedModel struct stays as the in-memory shape derived
from the registry. The registry admin path and PUT /v1/admin/pricing are
untouched.

Frontend/docs: the landing pricing table and earnings calculator now source the
lineup live from /v1/models/catalog + /v1/pricing (static fallback = the two
served models). Public read endpoints (/v1/models/catalog, /v1/pricing) get
permissive CORS so the marketing site can read them cross-origin. README,
console-ui and benchmark scripts updated to Gemma 4 26B + GPT-OSS 20B; README
revenue split made consistent (100% during the public alpha).

Refs #257, #256
@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
d-inference Ready Ready Preview May 30, 2026 2:54am
d-inference-console-ui-dev Ready Ready Preview May 30, 2026 2:54am
d-inference-landing Ready Ready Preview May 30, 2026 2:54am

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 30, 2026

This PR removes a dead code function (seedModelCatalog) that was already disconnected from startup; no security-relevant logic is changed.

Trust Boundaries Touched

  • TB-001 (Consumer → Coordinator API) — main.go is in scope for HTTP server config
  • TB-008 (Coordinator → Payments) — main.go bootstraps billing/wallet setup

Threat Analysis

Threat Assessment
T-038 — Missing ReadHeaderTimeout/MaxHeaderBytes ℹ️ Neutral. The diff removes only the dead seedModelCatalog function. The HTTP server struct initialisation (lines ~496 in the pre-diff file) is untouched; ReadHeaderTimeout and MaxHeaderBytes remain absent. SEC-027 is still open.
T-032 — Solana mnemonic exfiltration ℹ️ Neutral. Wallet/KMS bootstrapping code is not modified.

New Attack Surface

None introduced. The removed function was already unreachable from main() (per the comment in the diff itself) and contained only read/delete calls against the model catalog store — no cryptographic material, no auth logic, no network calls.

Open Findings Resolved

None. SEC-027, SEC-006, and all other findings flagged against main.go remain open and are unaffected by this change.


Reviewer note: The 22 files listed as not covered by a threat pattern (e.g. coordinator/api/server.go, billing_handlers.go, store/postgres.go) were not included in the diff provided for review. If those files contain substantive changes they should be diffed separately — several map directly to high-severity open findings (SEC-012 in billing_handlers.go → T-030, SEC-034 in server.go → T-006).


🔐 Threat model: docs/threat-model.yaml · Updates on each push to this PR

The README, landing pricing table / earnings calculator, and the console models
comparison were showing the $0.05/$0.20 platform fallback. Set them to the
intended ~50%-of-OpenRouter rates:

- Gemma 4 26B: $0.03 in / $0.165 out  (OpenRouter google/gemma-4-26b-a4b-it: $0.06/$0.33)
- GPT-OSS 20B: $0.015 in / $0.07 out  (OpenRouter openai/gpt-oss-20b: $0.03/$0.14)

console baselinePricing now holds the real OpenRouter output rates (330000 /
140000 micro-USD) so the "50% lower" comparison reads correctly. Live platform
pricing must still be set in the coordinator (PUT /v1/admin/pricing or the
register-model workflow) to match these docs; values now persist across restarts
after the model_prices cleanup fix.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ed3729ec66

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coordinator/api/server.go
Comment on lines +1862 to +1865
if publicCORSPaths[r.URL.Path] {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve credentialed CORS for pricing mutations

When a browser preflights DELETE /v1/pricing to revert a provider price, this branch is selected solely by path and returns Access-Control-Allow-Methods: GET, OPTIONS with no Authorization header or credentials support, even though the same path is also registered for authenticated pricing mutations. That preflight will be rejected before handleDeletePricing can run; gate the wildcard/public CORS behavior on the actual requested method (including Access-Control-Request-Method) or keep the credentialed CORS headers for non-GET pricing requests.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant