fix(pricing): let custom-pricing.json express a free model - #1069
Merged
Conversation
custom-pricing.json is the documented escape hatch for models no upstream dataset publishes, but it required at least one rate to be POSITIVE. A free tier could therefore not be expressed at all: the user knows the price is $0, the file refuses to record it, and the usage stays unpriced and excluded from submission with no way to correct it (#1021). This is the same free-vs-absent confusion as the Cursor cache-creation gap: 0.0 is an assertion ("this is free"), None is an absence ("unknown"). The validation now rejects only absence, which is the case that genuinely says nothing — reading a rate-less row as free would invent a $0 total for unknown pricing. Negative rates are still rejected; a negative price is nonsense rather than a statement about cost. The reporter's other two asks are already resolved and need no code here: #1053 made unpriced rows exclude-with-warning instead of failing the batch, which is the `--prune-unpriced` behaviour they asked for, as the default; and `zai/glm-4.5-flash` resolves today (verified), so the missing `zai/` provider prefix they reported is no longer reproducible. Confidence: high Scope-risk: narrow Directive: 0.0 and None are not interchangeable in pricing rows anywhere in this crate — 0.0 prices, None excludes Not-tested: whether a user-declared free model should suppress the "excluded unpriced" warning for sibling rows of the same model
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
The loader now accepts `0` so users can declare a free model, but all four
READMEs still told them at least one rate "must be present and positive".
Anyone following the documented contract would therefore never reach the
escape hatch this was added for.
The wording draws the distinction the code now makes: an explicit `0` is a
statement ("this costs nothing"), while an omitted field means the rate is
unknown and leaves the usage unpriced.
Confidence: high
Scope-risk: narrow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses the one genuinely-open ask in #1021.
Problem
custom-pricing.jsonis the documented escape hatch for models no upstream dataset publishes. Butcustom.rsrequired at least one rate to be positive:So a free tier was unexpressable. The user knows the price is $0, the file refuses to record it, and the usage stays unpriced — excluded from submission with no way to correct it. As @sigco3111 put it: "I can't use the documented escape hatch for free-tier models."
Fix
Reject only absence, not zero.
0.0is an assertion — "this model is free".Noneis an absence — "unknown". Reading a rate-less row as free would invent a $0 total for genuinely unknown pricing, so that case still errors.This is the same free-vs-absent confusion as the Cursor cache-creation gap in #1067, at the user-facing layer instead of the built-in table.
Negative rates still rejected — a negative price is nonsense, not a statement about cost.
The reporter's other two asks need no code
--prune-unpriced(their open question 2) — fix(submit): exclude unpriced usage without aborting #1053 made unpriced rows exclude-with-warning instead of failing the batch, and made it the default rather than a flag. Shipped in v4.11.0.zai/provider prefix — no longer reproducible. Verified:tokscale pricing zai/glm-4.5-flash→zai/glm-4.5-flash, and bareglm-4.5-flashresolves to the same key.Their first ask — a subscription-pricing carrier for
github-copilot/— is a larger design question I'd rather not fold in here; Copilot already prices at the underlying model's rates (github_copilot/gpt-4o→gpt-4o), so the gap is narrower than when it was filed.Tests
cargo test -p tokscale-core --lib— 1,490 passed, 0 failedclippy --all-targets -- -D warningscleandrops_entries_with_zero_pricesrenamed tokeeps_free_models_but_still_drops_negative_pricesand itsall-zeroassertion inverted; it now asserts the row loads withSome(0.0)on both rates, while the negative-price case stays droppedSummary by cubic
Allow
custom-pricing.jsonto declare free models by accepting 0.0 token rates, so free-tier usage is priced at $0 instead of excluded. Updated READMEs to document that an explicit zero is allowed and different from an omitted (unknown) field.0.0for input/output rates; only error if both rates are absent.Some(0.0)and negative-rate rows are dropped.README.md,README.ja.md,README.ko.md,README.zh-cn.mdto explain that0means free while omission means unknown.Written for commit ca39b51. Summary will update on new commits.