Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ public 50 the old detector named an example's `samples.csv` and the input
a fresh user-visible authorization instead. Credentials stored before this
release are bound on their next successful refresh. Reported by Saku0512
(CWE-522).
- **A price tier now begins where it says it does.** Its start came from the
tier above ending, so a gap in the catalog charged the higher rate for
prompts too small to have reached it. A base tier written as `min: 1` is also
recognised now, instead of dropping the model from pricing altogether.
- **Listing files now returns them in a stable order.** Files written at the
same moment — by a checkout, a build, or a script — share a timestamp, so
their order came from the filesystem and two identical searches could list
Expand Down
10 changes: 8 additions & 2 deletions backend/cli/src/provider/managed-pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ export namespace ManagedPricing {
if (!MANAGED_OPENROUTER_MODEL_SET.has(model.id) || model.available === false) continue
const pricing = metadata(model.pricing, model.hosting_provider, model.upstream_provider)
if (!pricing) continue
const first = model.pricing.tiers.find((tier) => !tier.min_input_tokens)
// Token counts start at 1, so an explicit min of 1 is the base tier and
// means the same as omitting it. Looking it up by falsiness missed that
// spelling and dropped the whole model out of the catalog.
const first = model.pricing.tiers.find((tier) => !tier.min_input_tokens || tier.min_input_tokens <= 1)
if (!first || (first.input === 0 && first.output === 0)) continue
const cost = (tier: z.infer<typeof Tier>) => ({
input: tier.input,
Expand All @@ -116,7 +119,10 @@ export namespace ManagedPricing {
prices
.map((tier, index) => ({
...tier,
threshold: prices[index - 1]?.max_input_tokens ?? (tier.min_input_tokens ?? 1) - 1,
// A tier applies from its own min. The previous tier's max is the
// same number only when the catalog is contiguous, and preferring it
// charged a tier's rate from as early as the tier above ended.
threshold: (tier.min_input_tokens ?? 1) - 1,
}))
.filter((tier) => tier.min_input_tokens !== undefined && tier.threshold > 0)
.map((tier) => ({ ...cost(tier), threshold: tier.threshold }))
Expand Down
60 changes: 60 additions & 0 deletions backend/cli/test/provider/managed-pricing-tiers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, expect, test } from "bun:test"
import { ManagedPricing } from "../../src/provider/managed-pricing"

const model = (tiers: unknown[]) => ({
id: "anthropic/claude-opus-5.5",
context_length: 1_000_000,
upstream_provider: "anthropic",
hosting_provider: "anthropic",
pricing: { funding_fee_bps: 0, tiers },
})

const price = (tiers: unknown[]) => ManagedPricing.parse({ models: [model(tiers)] })["anthropic/claude-opus-5.5"]

describe("ManagedPricing tier thresholds", () => {
test("a tier starts where it declares, even when the previous one ends earlier", () => {
// The threshold came from the previous tier's max, so a gap in the catalog
// charged the 201+ rate from 101 tokens up.
const gap = price([
{ input: 2, output: 12, max_input_tokens: 100 },
{ input: 4, output: 18, min_input_tokens: 201 },
])
expect(gap?.cost.tiers?.[0]?.threshold).toBe(200)
})

test("contiguous tiers are unaffected", () => {
const contiguous = price([
{ input: 2, output: 12, max_input_tokens: 100 },
{ input: 4, output: 18, min_input_tokens: 101 },
])
expect(contiguous?.cost.tiers?.[0]?.threshold).toBe(100)
})

test("a selection above the gap but below the tier's min does not pick that tier", () => {
const gap = price([
{ input: 2, output: 12, max_input_tokens: 100 },
{ input: 4, output: 18, min_input_tokens: 201 },
])
const threshold = gap?.cost.tiers?.[0]?.threshold ?? 0
// Selection elsewhere is `promptTokens > threshold`, so a 150-token prompt
// must not satisfy a tier that only starts at 201.
expect(150 > threshold).toBe(false)
expect(201 > threshold).toBe(true)
})

test("an explicit base min of 1 still prices the model", () => {
// Token counts start at 1, so `min: 1` is the base tier, identical to an
// omitted min. The base was looked up with `!tier.min_input_tokens`, which
// is false for 1, so the model vanished from the catalog entirely.
const explicit = price([
{ input: 2, output: 12, min_input_tokens: 1 },
{ input: 4, output: 18, min_input_tokens: 200_001 },
])
const omitted = price([
{ input: 2, output: 12 },
{ input: 4, output: 18, min_input_tokens: 200_001 },
])
expect(explicit?.cost.input).toBe(2)
expect(explicit?.cost).toEqual(omitted?.cost)
})
})
Loading