Skip to content

fix(pricing): match free-model names exactly, not by substring - #648

Merged
FlyM1ss merged 2 commits into
Open-Finance-Lab:mainfrom
subidhkhanal:fix/free-model-markers-exact-match
Oct 22, 2026
Merged

FlyM1ss merged 2 commits into
Open-Finance-Lab:mainfrom
subidhkhanal:fix/free-model-markers-exact-match

Conversation

@subidhkhanal

Copy link
Copy Markdown
Contributor

Fixes #590

Problem. _FREE_MODEL_MARKERS was matched by substring in _table_price and is_free_model, so any model id containing none, local, demo or baseline priced at $0/$0. Examples are acme/nonexistent, *-localized-* and *-demo-*. On the platform lane that $0 PricingSnapshot gave a reservation ceiling of 0.

Change. Both functions now match whole names (name in _FREE_MODEL_MARKERS), the rule listed_price_for_model has used since #589.

Model id Before After
acme/nonexistent $0 / $0 $1 / $5 (default)
vendor/model-localized-v1 $0 / $0 $1 / $5
acme/demo-7b $0 / $0 $1 / $5
local-model, none, rule-based, … $0 / $0 $0 / $0 (unchanged)

Impact. No configured name changes price. The leaderboard model_ids are real catalog slugs, baselines pass None, and the defaults are the exact sentinels. Only free-text names containing a marker move from $0 to a real price: external-run cost estimates (external_run_service.py:856), research settlement from an agent-reported model (research.py:530, still capped by the reservation), and the Discord bot, which now forwards such a name instead of dropping it. discord_bot.py:40 still drops the exact local-model sentinel.

Not done: the :free suffix from the issue sketch. It is left out on purpose:

  • test_pricing.py:84 pins price_for_model("openai/gpt-5.5:free") == (5.0, 30.0). Billing deliberately keeps the paid rate so a reservation never gets a zero ceiling.
  • In is_free_model, it would make the Discord bot drop real OpenRouter :free models instead of forwarding them.

listed_price_for_model keeps its own :free rule. Happy to add it if you want it; it's a small follow-up.

Tests. New cases in test_pricing.py cover:

  • ids containing a marker are no longer free;
  • the exact sentinels, including case and whitespace variants, are still free;
  • a :free id is still a real model to is_free_model.

The pricing and token-cost tests pass (47). In a local full-suite run on Windows, every failure also fails on unmodified main, so none come from this change.

marker ("none", "demo") and were priced at $0. Now only exact names
like "local-model" or "none" are free.
@vercel

vercel Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

@subidhkhanal is attempting to deploy a commit to the allan-feng's projects Team on Vercel.

A member of the Team first needs to authorize it.

Review follow-ups for the exact-match change:
- List rule-based-demo (the example client's default --model-name) as free;
  it was free only via the substring rule, so the Discord bot would forward
  it as a model and its runs would show a $1/$5 estimate.
- Price no model at all at $0, agreeing with is_free_model. An empty id
  cannot reach a reservation (PricingSnapshot.model_id min_length=1).
- One _is_sentinel check behind is_free_model, price_for_model and
  listed_price_for_model.
- RULE_BASED_MODEL_LABELS shared with provenance, as a subset of
  FREE_MODEL_NAMES (provenance behaviour unchanged).
- Bump PRICING_SOURCE_VERSION; a test pins it to a fingerprint of the
  table, the free names and the default so it cannot lag a price change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@FlyM1ss
FlyM1ss merged commit d3cfc33 into Open-Finance-Lab:main Oct 22, 2026
7 checks passed
@subidhkhanal
subidhkhanal deleted the fix/free-model-markers-exact-match branch October 22, 2026 18:18
@subidhkhanal
subidhkhanal restored the fix/free-model-markers-exact-match branch October 22, 2026 18:18
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.

billing: free-model markers match inside real model ids

2 participants