Skip to content

feat(catalog): add optional tiered-price and cache-write price fields - #416

Merged
argszero merged 1 commit into
mainfrom
feat/catalog-tiered-price-fields
Oct 16, 2026
Merged

argszero merged 1 commit into
mainfrom
feat/catalog-tiered-price-fields

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Adds the expression capability and validation for two price features the catalogue
currently cannot express (rant 2026-10-16T14:08:14, 【计价】tiered input price +
cache-write price):

  1. Per-request input-length tiers — an optional [[models.tiers]] array:

    [[models.tiers]]
    from_tokens = 512001
    input_per_m = 4.20
    cache_hit_input_per_m = 0.84
    output_per_m = 16.80

    from_tokens is the inclusive lower bound of the tier; the model's own
    input_per_m / cache_hit_input_per_m / output_per_m are the first tier.

  2. Cache-write price — an optional cache_write_input_per_m field, defaulting to
    0 ("cache writes are free", i.e. today's behaviour).

Both fields are #[serde(default)], so every existing catalogue row parses exactly as
before and pricing is byte-for-byte unchanged — settle is deliberately not touched
in this step.

Config::validate gains the rules that the data itself cannot be trusted to satisfy:
tier from_tokens must be > 0 and strictly increasing, and no tier price (or
cache_write_input_per_m) may be negative. Tier data goes straight into real money, so
a malformed catalogue is rejected at load time instead of mis-pricing silently.

Scope — this is step 1 of 2

The rant asks for two steps; this PR is the first:

  • Step 1 (this PR): the fields exist, parse and validate. Nothing in the pricing
    path reads them yet.
  • Step 2 (follow-up): wire them into billing — propagate the tier data through
    src/db.rs::seed_models into the models table and select the tier by the request's
    input-token count in settle, and per-tier boundary tests for each vendor.

The tests below pin the backward-compatibility half of the rant's acceptance criteria
(no-tier rows price identically — settle and its expectations are untouched, suite
unchanged counts).

Related Issue

Rant 2026-10-16T14:08:14 (【计价】按输入长度分档的价格、以及缓存写入价,都没有字段).

Tests

  • Existing tests pass — cargo test: 571 passed / 0 failed (564 before; the 7
    new tests are below)
  • New tests added:
    • parse_model_tier_from_toml — a tier parses field-by-field; cache_hit_input_per_m defaults to 0
    • model_defaults_to_no_tiers_and_zero_cache_write — every example row defaults to empty tiers / zero cache-write price
    • validate_accepts_strictly_increasing_tiers — positive control
    • validate_rejects_non_increasing_tier_boundary — duplicate and decreasing boundaries
    • validate_rejects_zero_tier_boundary
    • validate_rejects_negative_tier_price
    • validate_rejects_negative_cache_write_price
  • cargo fmt clean
  • cargo clippy --all-targets -- -D warnings clean
  • non-test build clean (RUSTFLAGS="-D warnings" cargo build) — the only witness
    that the new fields are read by production code, not just tests

Models can now express per-request input-length tiers ([[models.tiers]])
and a cache-write price (cache_write_input_per_m). Both are optional and
default off, so a catalogue without them is unchanged; validation rejects
non-increasing from_tokens, zero/negative tier boundaries and negative
prices. Wiring these into settle is a follow-up step.

Ref: rant 2026-10-16T14:08:14 (tiered input pricing)
@argszero
argszero merged commit 496a723 into main Oct 16, 2026
2 checks passed
@argszero
argszero deleted the feat/catalog-tiered-price-fields branch October 16, 2026 22:46
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