From 16df60e7da17091a034337b02893a495d025a84f Mon Sep 17 00:00:00 2001 From: pxys-io Date: Fri, 3 Apr 2026 05:36:47 +0200 Subject: [PATCH] feat: add context_tiers for arbitrary tiered pricing based on context length Replace the hardcoded context_over_200k with a flexible context_tiers array that allows providers to define pricing tiers at arbitrary token thresholds, matching OpenRouter's tiered pricing format. - Add ContextTier schema with min_context, input, output, cache_read, cache_write - Keep backward compatibility with deprecated context_over_200k - Add validation that tiers must be sorted by min_context ascending - Update xiaomi/mimo-v2-pro with example 3-tier pricing (base, 200k, 500k) OpenRouter docs: https://openrouter.ai/docs/guides/get-started/for-providers Co-authored-by: Qwen-Coder --- packages/core/src/schema.ts | 70 +++++++++++++++++++ .../openrouter/models/xiaomi/mimo-v2-pro.toml | 15 ++++ 2 files changed, 85 insertions(+) diff --git a/packages/core/src/schema.ts b/packages/core/src/schema.ts index 55b118451..a46e70023 100644 --- a/packages/core/src/schema.ts +++ b/packages/core/src/schema.ts @@ -23,6 +23,29 @@ const Cost = z.object({ .min(0, "Audio output price cannot be negative") .optional(), }); + +/** + * A pricing tier that applies when the total input tokens (input + cache_read) + * meet or exceed the min_context threshold. Matches OpenRouter's tiered pricing + * format: https://openrouter.ai/docs/guides/get-started/for-providers + */ +const ContextTier = z.object({ + min_context: z + .number() + .int() + .positive("min_context must be a positive integer (token count)"), + input: z.number().min(0, "Input price cannot be negative"), + output: z.number().min(0, "Output price cannot be negative"), + cache_read: z + .number() + .min(0, "Cache read price cannot be negative") + .optional(), + cache_write: z + .number() + .min(0, "Cache write price cannot be negative") + .optional(), +}); + export const Model = z .object({ id: z.string(), @@ -61,7 +84,36 @@ export const Model = z }), open_weights: z.boolean(), cost: Cost.extend({ + /** + * @deprecated Use `context_tiers` instead. + * Kept for backward compatibility — consumers should merge this into + * context_tiers if present. + */ context_over_200k: Cost.optional(), + /** + * Ordered array of pricing tiers that apply when total input tokens + * (input + cache_read) meet or exceed the tier's min_context threshold. + * Only the highest matching tier is applied. + * + * Example (TOML): + * ```toml + * [[cost.context_tiers]] + * min_context = 200_000 + * input = 2.00 + * output = 6.00 + * cache_read = 0.40 + * + * [[cost.context_tiers]] + * min_context = 500_000 + * input = 4.00 + * output = 12.00 + * cache_read = 0.80 + * ``` + */ + context_tiers: z + .array(ContextTier) + .max(4, "Maximum 4 context tiers supported") + .optional(), }).optional(), limit: z.object({ context: z.number().min(0, "Context window must be positive"), @@ -86,6 +138,24 @@ export const Model = z message: "Cannot set cost.reasoning when reasoning is false", path: ["cost", "reasoning"], }, + ) + .refine( + (data) => { + const tiers = data.cost?.context_tiers; + if (!tiers || tiers.length < 2) return true; + // Ensure tiers are sorted by min_context ascending + for (let i = 1; i < tiers.length; i++) { + if (tiers[i]!.min_context <= tiers[i - 1]!.min_context) { + return false; + } + } + return true; + }, + { + message: + "context_tiers must be sorted by min_context in ascending order", + path: ["cost", "context_tiers"], + }, ); export type Model = z.infer; diff --git a/providers/openrouter/models/xiaomi/mimo-v2-pro.toml b/providers/openrouter/models/xiaomi/mimo-v2-pro.toml index bd133c71f..6dff14b6d 100644 --- a/providers/openrouter/models/xiaomi/mimo-v2-pro.toml +++ b/providers/openrouter/models/xiaomi/mimo-v2-pro.toml @@ -13,6 +13,21 @@ open_weights = true input = 1.00 output = 3.00 +# Tiered pricing based on context length (matches OpenRouter pricing tiers) +# Tier 1: applies when input + cache_read tokens >= 200,000 +[[cost.context_tiers]] +min_context = 200_000 +input = 2.00 +output = 6.00 +cache_read = 0.40 + +# Tier 2: applies when input + cache_read tokens >= 500,000 +[[cost.context_tiers]] +min_context = 500_000 +input = 4.00 +output = 12.00 +cache_read = 0.80 + [limit] context = 1_048_576 output = 65_536