mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-24 06:33:01 -04:00
refactor(opencode): remove legacy 200k pricing
This commit is contained in:
@@ -39,14 +39,6 @@ const Cost = Schema.Struct({
|
||||
cache_read: Schema.optional(Schema.Finite),
|
||||
cache_write: Schema.optional(Schema.Finite),
|
||||
tiers: Schema.optional(Schema.Array(CostTier)),
|
||||
context_over_200k: Schema.optional(
|
||||
Schema.Struct({
|
||||
input: Schema.Finite,
|
||||
output: Schema.Finite,
|
||||
cache_read: Schema.optional(Schema.Finite),
|
||||
cache_write: Schema.optional(Schema.Finite),
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
const ReasoningOption = Schema.Union([
|
||||
|
||||
@@ -30,22 +30,6 @@ function cost(input: ModelsDev.Model["cost"]): ModelV2Info["cost"] {
|
||||
write: item.cache_write ?? 0,
|
||||
},
|
||||
})) ?? []),
|
||||
...(input?.context_over_200k
|
||||
? [
|
||||
{
|
||||
tier: {
|
||||
type: "context" as const,
|
||||
size: 200_000,
|
||||
},
|
||||
input: input.context_over_200k.input,
|
||||
output: input.context_over_200k.output,
|
||||
cache: {
|
||||
read: input.context_over_200k.cache_read ?? 0,
|
||||
write: input.context_over_200k.cache_write ?? 0,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -225,22 +225,11 @@ function withoutCredentials(body: Readonly<Record<string, unknown>> | undefined)
|
||||
}
|
||||
|
||||
function remoteCost(input: NonNullable<(typeof ConfigProviderV1.Model.Type)["cost"]>) {
|
||||
const base = {
|
||||
input: input.input,
|
||||
output: input.output,
|
||||
cache: { read: input.cache_read ?? 0, write: input.cache_write ?? 0 },
|
||||
}
|
||||
if (!input.context_over_200k) return [base]
|
||||
return [
|
||||
base,
|
||||
{
|
||||
tier: { type: "context" as const, size: 200_000 },
|
||||
input: input.context_over_200k.input,
|
||||
output: input.context_over_200k.output,
|
||||
cache: {
|
||||
read: input.context_over_200k.cache_read ?? 0,
|
||||
write: input.context_over_200k.cache_write ?? 0,
|
||||
},
|
||||
input: input.input,
|
||||
output: input.output,
|
||||
cache: { read: input.cache_read ?? 0, write: input.cache_write ?? 0 },
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -199,16 +199,6 @@ function migrateModel(info: typeof ConfigProviderV1.Model.Type, packageName?: st
|
||||
output: info.cost.output,
|
||||
cache: { read: info.cost.cache_read, write: info.cost.cache_write },
|
||||
},
|
||||
...(info.cost.context_over_200k
|
||||
? [
|
||||
{
|
||||
tier: { type: "context" as const, size: 200_000 },
|
||||
input: info.cost.context_over_200k.input,
|
||||
output: info.cost.context_over_200k.output,
|
||||
cache: { read: info.cost.context_over_200k.cache_read, write: info.cost.context_over_200k.cache_write },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
const capabilities =
|
||||
info.tool_call !== undefined || info.modalities?.input !== undefined || info.modalities?.output !== undefined
|
||||
|
||||
@@ -34,14 +34,6 @@ export const Model = Schema.Struct({
|
||||
output: Schema.Finite,
|
||||
cache_read: Schema.optional(Schema.Finite),
|
||||
cache_write: Schema.optional(Schema.Finite),
|
||||
context_over_200k: Schema.optional(
|
||||
Schema.Struct({
|
||||
input: Schema.Finite,
|
||||
output: Schema.Finite,
|
||||
cache_read: Schema.optional(Schema.Finite),
|
||||
cache_write: Schema.optional(Schema.Finite),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
),
|
||||
limit: Schema.optional(
|
||||
|
||||
@@ -61,7 +61,6 @@ describe("ModelsDevPlugin", () => {
|
||||
cache_read: 0.25,
|
||||
},
|
||||
],
|
||||
context_over_200k: { input: 5, output: 22.5, cache_read: 0.5 },
|
||||
},
|
||||
limit: { context: 1_050_000, input: 922_000, output: 128_000 },
|
||||
experimental: {
|
||||
@@ -114,12 +113,6 @@ describe("ModelsDevPlugin", () => {
|
||||
output: 18,
|
||||
cache: { read: 0.25, write: 0 },
|
||||
},
|
||||
{
|
||||
tier: { type: "context", size: 200_000 },
|
||||
input: 5,
|
||||
output: 22.5,
|
||||
cache: { read: 0.5, write: 0 },
|
||||
},
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1035,13 +1035,6 @@ const ProviderCost = Schema.Struct({
|
||||
output: Schema.Finite,
|
||||
cache: ProviderCacheCost,
|
||||
tiers: optional(Schema.Array(ProviderCostTier)),
|
||||
experimentalOver200K: optional(
|
||||
Schema.Struct({
|
||||
input: Schema.Finite,
|
||||
output: Schema.Finite,
|
||||
cache: ProviderCacheCost,
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
const ProviderLimit = Schema.Struct({
|
||||
@@ -1213,16 +1206,6 @@ function cost(c: ModelsDev.Model["cost"]): Model["cost"] {
|
||||
tier: item.tier,
|
||||
}))
|
||||
}
|
||||
if (c?.context_over_200k) {
|
||||
result.experimentalOver200K = {
|
||||
cache: {
|
||||
read: c.context_over_200k.cache_read ?? 0,
|
||||
write: c.context_over_200k.cache_write ?? 0,
|
||||
},
|
||||
input: c.context_over_200k.input,
|
||||
output: c.context_over_200k.output,
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1528,6 +1511,7 @@ const layer = Layer.effect(
|
||||
read: model?.cost?.cache_read ?? existingModel?.cost?.cache.read ?? 0,
|
||||
write: model?.cost?.cache_write ?? existingModel?.cost?.cache.write ?? 0,
|
||||
},
|
||||
tiers: existingModel?.cost.tiers,
|
||||
},
|
||||
options: mergeDeep(existingModel?.options ?? {}, model.options ?? {}),
|
||||
limit: {
|
||||
|
||||
@@ -380,10 +380,7 @@ export const getUsage = (input: { model: Provider.Model; usage: Usage; metadata?
|
||||
const costInfo =
|
||||
input.model.cost?.tiers
|
||||
?.filter((item) => item.tier.type === "context" && contextTokens > item.tier.size)
|
||||
.sort((a, b) => b.tier.size - a.tier.size)[0] ??
|
||||
(input.model.cost?.experimentalOver200K && contextTokens > 200_000
|
||||
? input.model.cost.experimentalOver200K
|
||||
: input.model.cost)
|
||||
.sort((a, b) => b.tier.size - a.tier.size)[0] ?? input.model.cost
|
||||
const totalNanoAiu = input.metadata?.["copilot"]?.["totalNanoAiu"]
|
||||
return {
|
||||
cost:
|
||||
|
||||
@@ -566,6 +566,27 @@ it.instance(
|
||||
},
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"model config preserves catalog cost tiers",
|
||||
Effect.gen(function* () {
|
||||
yield* set("REQUESTY_API_KEY", "test-api-key")
|
||||
const providers = yield* list
|
||||
expect(providers[ProviderV2.ID.make("requesty")].models["google/gemini-2.5-pro"].cost.tiers).toEqual([
|
||||
{
|
||||
input: 2.5,
|
||||
output: 15,
|
||||
cache: { read: 0.25, write: 0 },
|
||||
tier: { type: "context", size: 200_000 },
|
||||
},
|
||||
])
|
||||
}),
|
||||
{
|
||||
config: {
|
||||
provider: { requesty: { models: { "google/gemini-2.5-pro": {} } } },
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"model config preserves explicitly empty models.dev variants",
|
||||
Effect.gen(function* () {
|
||||
@@ -1401,11 +1422,14 @@ test("mode options and cost are derived from the base model", () => {
|
||||
input: 2.5,
|
||||
output: 15,
|
||||
cache_read: 0.25,
|
||||
context_over_200k: {
|
||||
input: 5,
|
||||
output: 22.5,
|
||||
cache_read: 0.5,
|
||||
},
|
||||
tiers: [
|
||||
{
|
||||
input: 5,
|
||||
output: 22.5,
|
||||
cache_read: 0.5,
|
||||
tier: { type: "context", size: 272_000 },
|
||||
},
|
||||
],
|
||||
},
|
||||
limit: {
|
||||
context: 1_050_000,
|
||||
@@ -1449,11 +1473,14 @@ test("mode options and cost are derived from the base model", () => {
|
||||
const pro = Provider.fromModelsDevProvider(provider).models["gpt-5.6-sol-pro"]
|
||||
expect(pro.api.id).toEqual("gpt-5.6-sol")
|
||||
expect(pro.options).toEqual({ reasoningMode: "pro", serviceTier: "priority" })
|
||||
expect(model.cost.experimentalOver200K).toEqual({
|
||||
input: 5,
|
||||
output: 22.5,
|
||||
cache: { read: 0.5, write: 0 },
|
||||
})
|
||||
expect(model.cost.tiers).toEqual([
|
||||
{
|
||||
input: 5,
|
||||
output: 22.5,
|
||||
cache: { read: 0.5, write: 0 },
|
||||
tier: { type: "context", size: 272_000 },
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("models.dev normalization fills required response fields", () => {
|
||||
|
||||
@@ -1830,7 +1830,7 @@ describe("SessionNs.getUsage", () => {
|
||||
expect(result.cost).toBe(0.04473525)
|
||||
})
|
||||
|
||||
test("uses matching context cost tier before over-200k fallback", () => {
|
||||
test("uses the matching context cost tier", () => {
|
||||
const model = createModel({
|
||||
context: 1_000_000,
|
||||
output: 32_000,
|
||||
@@ -1852,11 +1852,6 @@ describe("SessionNs.getUsage", () => {
|
||||
tier: { type: "context", size: 500_000 },
|
||||
},
|
||||
],
|
||||
experimentalOver200K: {
|
||||
input: 100,
|
||||
output: 100,
|
||||
cache: { read: 100, write: 100 },
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = SessionNs.getUsage({
|
||||
@@ -1873,7 +1868,7 @@ describe("SessionNs.getUsage", () => {
|
||||
expect(result.cost).toBe(2.75 + 0.6 + 0.05)
|
||||
})
|
||||
|
||||
test("falls back to over-200k pricing when no cost tier matches", () => {
|
||||
test("uses base pricing when no context cost tier matches", () => {
|
||||
const model = createModel({
|
||||
context: 1_000_000,
|
||||
output: 32_000,
|
||||
@@ -1889,11 +1884,6 @@ describe("SessionNs.getUsage", () => {
|
||||
tier: { type: "context", size: 500_000 },
|
||||
},
|
||||
],
|
||||
experimentalOver200K: {
|
||||
input: 3,
|
||||
output: 4,
|
||||
cache: { read: 0.3, write: 1.5 },
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = SessionNs.getUsage({
|
||||
@@ -1901,7 +1891,7 @@ describe("SessionNs.getUsage", () => {
|
||||
usage: usage({ inputTokens: 300_000, outputTokens: 100_000, totalTokens: 400_000 }),
|
||||
})
|
||||
|
||||
expect(result.cost).toBe(0.9 + 0.4)
|
||||
expect(result.cost).toBe(0.3 + 0.2)
|
||||
})
|
||||
|
||||
test.each(["@ai-sdk/anthropic", "@ai-sdk/amazon-bedrock", "@ai-sdk/google-vertex/anthropic"])(
|
||||
|
||||
@@ -1781,12 +1781,6 @@ export type ProviderConfig = {
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
context_over_200k?: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
}
|
||||
}
|
||||
limit?: {
|
||||
context: number
|
||||
@@ -2083,14 +2077,6 @@ export type Model = {
|
||||
size: number
|
||||
}
|
||||
}>
|
||||
experimentalOver200K?: {
|
||||
input: number
|
||||
output: number
|
||||
cache: {
|
||||
read: number
|
||||
write: number
|
||||
}
|
||||
}
|
||||
}
|
||||
limit: {
|
||||
context: number
|
||||
|
||||
@@ -20863,25 +20863,6 @@
|
||||
},
|
||||
"cache_write": {
|
||||
"type": "number"
|
||||
},
|
||||
"context_over_200k": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "number"
|
||||
},
|
||||
"output": {
|
||||
"type": "number"
|
||||
},
|
||||
"cache_read": {
|
||||
"type": "number"
|
||||
},
|
||||
"cache_write": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["input", "output"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["input", "output"],
|
||||
@@ -21738,32 +21719,6 @@
|
||||
"required": ["input", "output", "cache", "tier"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"experimentalOver200K": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "number"
|
||||
},
|
||||
"output": {
|
||||
"type": "number"
|
||||
},
|
||||
"cache": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"read": {
|
||||
"type": "number"
|
||||
},
|
||||
"write": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["read", "write"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["input", "output", "cache"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["input", "output", "cache"],
|
||||
|
||||
Reference in New Issue
Block a user