Models & providersAuto-sync

Auto-sync

The gateway rebuilds its model registry from every provider every 6 hours.

CleverRouter's model registry is not hand-maintained. The gateway rebuilds it from every configured provider's catalog every 6 hours. New models appear without an SDK release; retired models are flagged automatically.

At a glance

Jobmodel-sync.ts in the gateway
CadenceEvery 6 hours
Crash policyPer-provider failures don't kill the tick
Deprecated markerdeprecated_at = now(), returns 410 Gone

What the cron job does

Every 6 hours, the gateway runs model-sync.ts:

  1. Call listModels() on each configured provider (Scaleway, Tensorix, Bedrock).
  2. UPSERT into the models and model_providers tables.
  3. Track discoveredAt, lastSeenAt, autoSynced, pricingKnown, lifecycle (active / legacy / preview).
  4. Models present last sync but missing in the current one (with autoSynced=true) are flagged deprecated_at = now() and active=false.

The job is crash-resistant: a single provider failure doesn't kill the tick, a running flag prevents overlap, SIGTERM triggers a clean shutdown.

Discovery vs whitelisting

BehaviourAuto-synced modelManually seeded model
Appears after first syncyesseeded at deploy
Marked deprecated on missyesno
Touched by re-syncyesno (preserved)
Pricingfrom whitelist if presentfrom whitelist

Pricing lives in a whitelist in packages/db/src/seeds/pricing.ts. A model with no whitelist match has pricingKnown=false — the call still runs, but cost tracking shows €0 until pricing is added.

Deprecation flow

When a provider removes a model:

  1. The next sync sees autoSynced=true for that ID but no longer finds it upstream.
  2. The model is flagged deprecated_at = now(), active=false.
  3. Subsequent calls return 410 Gone with error.code: model_deprecated.
  4. The SDK throws CRModelDeprecatedError for typed handling.

Manually seeded models are never auto-deprecated — that lets us maintain "alias" entries pointing to current model IDs.

Forcing a sync

Platform admins can trigger an out-of-band sync at /app/platform/health ("Re-sync models" button). Internally that hits POST /admin/sync on the gateway, authorised via the X-Admin-Token header.

Defensive coding

active-models.ts
const all = await cr.listModels({ kind: 'chat' });
const stillActive = all.filter((m) => !(m as any).deprecated_at);

Or just handle CRModelDeprecatedError and pick a successor on the spot:

import { CRModelDeprecatedError } from '@cleverrouter/sdk';

try {
  await cr.chat({ model, messages });
} catch (err) {
  if (err instanceof CRModelDeprecatedError) {
    // model id was retired upstream. Pick something else.
  } else {
    throw err;
  }
}

Sync interval

6 hours is the default. Shorter intervals increase load on provider APIs without product benefit — model catalogs change on the order of days, not minutes.

  • Catalog — what shows up after a sync.
  • ErrorsCRModelDeprecatedError.
  • PricingpricingKnown=false semantics.