Skip to content

fix(providers): one payload free-evidence predicate for every /models reader - #14930

Merged
diegosouzapw merged 3 commits into
diegosouzapw:release/v3.8.51from
aldoeliacim:fix/discovery-free-evidence-input-output-pricing
Sep 29, 2026
Merged

diegosouzapw merged 3 commits into
diegosouzapw:release/v3.8.51from
aldoeliacim:fix/discovery-free-evidence-input-output-pricing

Conversation

@aldoeliacim

@aldoeliacim aldoeliacim commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ base-red inherited: #14496. Fast Quality Gates (mutation-test-coverage, dashboard-typecheck on NoAuthAccountCard.tsx) and the four Unit Tests fast-path shards fail identically on every open PR against release/v3.8.51 (checked #14924, #14929). The failing-test set on this PR's first CI run matched those PRs line for line (36/36, 0 unique to this PR), and the same gates fail locally on a clean a58000c76 worktree. Nothing here touches those files.

Summary

Every place that reads a provider's live /models entry to decide "is this free" now goes through a single predicate, hasPayloadFreeEvidence (src/shared/utils/payloadFreeEvidence.ts):

  • normalizeDiscoveredModels: the isFree persisted at model sync, which feeds the provider-page Free only filter and badge
  • isFreeModel / selectModelsForImport: the "import only free models" connection option
  • isOpenRouterFreeModel: the OpenRouter entries in /v1/models

Before this PR each one re-implemented the check, and all of them read only OpenRouter's pricing.prompt / pricing.completion.

Precedence, strongest first:

  1. An explicit boolean flag (isFree / is_free / free, top-level or inside pricing) is the provider's own verdict and wins both ways.
  2. A :free id suffix.
  3. Zero prompt/input and completion/output price, with no positive price anywhere else in pricing (per request, image, second, minute, …). Ratio fields (discount, multiplier, coefficient, thresholds) are not prices.
  4. A free tag, under the same no-positive-price condition.

The default Free-badge rule also honours explicit isFree === true on every provider, as strict mode already did. Without that, discovery evidence for providers outside FREE_MODEL_BUDGETS (e.g. vercel-ai-gateway) was dropped at render time.

Measured bug class (not a single instance)

On 2026-09-27 I fetched every public /models URL referenced by the provider registry (104 URLs, 47 distinct catalogs returned a model list without auth). I then ran the real normalizeDiscoveredModels from the base (a58000c76) and from this branch over the raw payloads:

Defect on base Providers Models
input/output pricing or a free tag ignored → free model never marked free Vercel AI Gateway, xKiro 34
Provider's isFree: false / free: false overridden by a 0/0 token price → marked free Kilo, LLM Gateway 48
0/0 token price on a model billed per request / image / second / minute → marked free OFox, FastRouter, EUrouter, MegaNova 23
Top-level free: true with no pricing object ignored g4f 1

Across the 47 catalogs the free set goes from 277 to 241 models: 35 added, 71 false positives removed, no other differences. The Vercel case that surfaced this (poolside/laguna-s-2.1-free, inclusionai/ling-3.0-flash-sante[-free], stealth/pixel-canary) is 4 of the 34.

Related Issues / prior art

Validation

  • Change type: provider model discovery / free-model detection
  • New tests fail on the base, pass here: payload-free-evidence.test.ts (8), plus 2 new cases in model-discovery-free-metadata.test.ts and 1 in free-badge-provider-gate.test.ts
  • All 134 unit files that import any touched module (node --import tsx/esm --test): 858 pass, 1 fail. The failure, noauth-catalog-only-providers, fails identically on the clean base and is in the inherited base-red set. The Vitest suite (autoCombo/free-regime-not-read-by-predicate) passes 7/7.
  • npm run typecheck:core → 0; npm run lint:json -- --max-warnings 0 → 0; prettier --check clean
  • The Fast Quality Gates list, run locally on this branch and on a clean base worktree: identical results (only mutation-test-coverage and dashboard-typecheck fail, on both, for files this PR does not touch)
  • Reconciled with the active release base release/v3.8.51 (0 commits behind)
  • npm run test:scoped reports "hub file changed — run the full suite", so the full shards are left to CI per the golden path

Reviewer Notes

  • Net behaviour change: fewer false "free" labels on billed models (71), more true ones (35). No routing, auth or hidePaidModels gate is loosened: those still require a documented provider free tier.
  • isZeroPrice stays exported from catalogOpenrouter.ts (covered by catalog-helpers-extraction.test.ts). The private copies in modelDiscovery.ts and freeModels.ts are gone.

… tags

Model discovery only read OpenRouter-style `pricing.prompt` /
`pricing.completion`. Vercel AI Gateway's /v1/models publishes
`pricing.input` / `pricing.output` and a `tags: ["free"]` array, so its
zero-priced models synced without `isFree` and never appeared under the
provider page's "Free only" filter.

- Read `input`/`output` as fallbacks for `prompt`/`completion`.
- Accept a `free` tag as live evidence unless the same payload publishes
  a non-zero price for that model.
- Let the default (non-strict) provider-page badge honor explicit
  `isFree === true` on every provider, as strict mode already does, so
  live discovery evidence is not dropped for providers outside the
  static free catalog.
Copilot AI lite review requested due to automatic review settings September 27, 2026 08:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

… reader

Discovery, the import-only-free filter and the OpenRouter catalog each
re-implemented "is this /models entry free", and each read only
OpenRouter's pricing.prompt/pricing.completion. Measured on 2026-09-27
across the 47 distinct public provider catalogs reachable from the
registry's /models URLs, that dropped real evidence in both directions:

- input/output pricing and `free` tags were ignored (Vercel AI Gateway,
  xKiro): 34 zero-priced models never marked free;
- a provider's explicit `isFree: false` / `free: false` was overridden by
  a 0/0 token price (Kilo, LLM Gateway): 48 models wrongly marked free;
- a 0/0 token price marked models billed per request, image, second or
  minute as free (EUrouter, FastRouter, MegaNova, OFox): 23 models;
- a top-level `free: true` with no pricing object was ignored (g4f): 1.

hasPayloadFreeEvidence (src/shared/utils/payloadFreeEvidence.ts) applies
one precedence to every reader: explicit boolean flag (top-level or in
pricing) > `:free` suffix > zero prompt/input and completion/output with
no positive price anywhere else in pricing > `free` tag under the same
condition. Ratio fields (discount, multiplier, coefficient, thresholds)
are not prices.

Across those 47 catalogs the free set goes from 277 to 241 models:
+35 detected, -71 false positives removed, no other differences.
@aldoeliacim aldoeliacim changed the title fix(providers): detect free models from input/output pricing and free tags fix(providers): one payload free-evidence predicate for every /models reader Sep 27, 2026
@diegosouzapw
diegosouzapw merged commit 6955fdd into diegosouzapw:release/v3.8.51 Sep 29, 2026
11 of 16 checks passed
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.

3 participants