Skip to content
Closed
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
18 changes: 18 additions & 0 deletions packages/opencode/src/provider/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export namespace ModelsDev {
output: z.number(),
cache_read: z.number().optional(),
cache_write: z.number().optional(),
/**
* @deprecated Use `context_tiers` instead.
*/
context_over_200k: z
.object({
input: z.number(),
Expand All @@ -55,6 +58,21 @@ export namespace ModelsDev {
cache_write: z.number().optional(),
})
.optional(),
/**
* Ordered array of pricing tiers that apply when total input tokens
* (input + cache_read) meet or exceed the tier's min_context threshold.
*/
context_tiers: z
.array(
z.object({
min_context: z.number(),
input: z.number(),
output: z.number(),
cache_read: z.number().optional(),
cache_write: z.number().optional(),
}),
)
.optional(),
})
.optional(),
limit: z.object({
Expand Down
34 changes: 34 additions & 0 deletions packages/opencode/src/provider/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,10 @@ export namespace Provider {
read: z.number(),
write: z.number(),
}),
/**
* @deprecated Use `contextTiers` instead.
* Kept for backward compatibility with older models.dev snapshots.
*/
experimentalOver200K: z
.object({
input: z.number(),
Expand All @@ -842,6 +846,25 @@ export namespace Provider {
}),
})
.optional(),
/**
* Ordered pricing tiers that apply when total input tokens
* (input + cache_read) meet or exceed the tier's min_context threshold.
* Consumers should select the highest tier whose min_context is <=
* the current total input tokens.
*/
contextTiers: z
.array(
z.object({
min_context: z.number(),
input: z.number(),
output: z.number(),
cache: z.object({
read: z.number(),
write: z.number(),
}),
}),
)
.optional(),
}),
limit: z.object({
context: z.number(),
Expand Down Expand Up @@ -928,6 +951,17 @@ export namespace Provider {
output: model.cost.context_over_200k.output,
}
: undefined,
contextTiers: model.cost?.context_tiers
? model.cost.context_tiers.map((tier) => ({
min_context: tier.min_context,
input: tier.input,
output: tier.output,
cache: {
read: tier.cache_read ?? 0,
write: tier.cache_write ?? 0,
},
}))
: undefined,
},
limit: {
context: model.limit.context,
Expand Down
26 changes: 22 additions & 4 deletions packages/opencode/src/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,28 @@ export namespace Session {
},
}

const costInfo =
input.model.cost?.experimentalOver200K && tokens.input + tokens.cache.read > 200_000
? input.model.cost.experimentalOver200K
: input.model.cost
const costInfo = (() => {
const totalInputTokens = tokens.input + tokens.cache.read;

// Prefer new context_tiers with arbitrary thresholds
if (input.model.cost?.contextTiers && input.model.cost.contextTiers.length > 0) {
// Find the highest tier whose min_context <= totalInputTokens
let selected = input.model.cost; // base cost (no tier)
for (const tier of input.model.cost.contextTiers) {
if (totalInputTokens >= tier.min_context) {
selected = tier;
}
}
return selected;
}

// Fall back to legacy experimentalOver200K (hardcoded 200K threshold)
if (input.model.cost?.experimentalOver200K && totalInputTokens > 200_000) {
return input.model.cost.experimentalOver200K;
}

return input.model.cost;
})();
return {
cost: safe(
new Decimal(0)
Expand Down
Loading