#17645 · @mollux · opened Mar 15, 2026 at 7:27 PM UTC · last updated Mar 21, 2026 at 4:51 PM UTC

fix(provider): apply config model cost overrides at runtime

appfix
69
+8412 files

Score breakdown

Impact

8.0

Clarity

9.0

Urgency

7.0

Ease Of Review

8.0

Guidelines

9.0

Readiness

8.0

Size

7.0

Trust

5.0

Traction

4.0

Summary

This PR fixes a bug where custom provider model cost overrides from the config were not applied at runtime, leading to incorrect cost tracking. The change ensures cost values are consistently used for usage calculations.

Open in GitHub

Description

Issue for this PR

Closes #17223

Type of change

  • [x] Bug fix
  • [ ] New feature
  • [ ] Refactor / code improvement
  • [ ] Documentation

What does this PR do?

This fixes a runtime gap where model prices from config could be present in resolved config but not applied on the final model object used for usage/cost calculations. The change applies config model cost overrides during provider model finalization so cost values are consistently present before runtime usage math runs.

How did you verify your code works?

  • Added/updated provider test coverage for model cost override behavior via config content.
  • Ran focused tests locally:
bun test test/provider/provider.test.ts -t "OPENCODE_CONFIG_CONTENT overrides cost for configured model|model cost overrides existing cost values"

Screenshots / recordings

N/A (non-UI change)

Checklist

  • [x] I have tested my changes locally
  • [x] I have not included unrelated changes in this PR

Linked Issues

#17223 Cost tracking ($ Spent) does not work for custom provider models

View issue

Comments

No comments.

Changed Files

packages/opencode/src/provider/provider.ts

+251
@@ -1083,6 +1083,7 @@ export namespace Provider {
const configProvider = config.provider?.[providerID]
for (const [modelID, model] of Object.entries(provider.models)) {
const configModel = configProvider?.models?.[modelID]
model.api.id = model.api.id ?? model.id ?? modelID
if (
modelID === "gpt-5-chat-latest" ||
@@ -1099,8 +1100,31 @@ export namespace Provider {
model.variants = mapValues(ProviderTransform.variants(model), (v) => v)
if (configModel?.cost) {
model.cost = {
...model.cost,
input: configModel.cost.input ?? model.cost.input,
output: configModel.cost.output ?? model.cost.output,
cache: {
read: configModel.cost.cache_read ?? model.cost.cache.read,
write: configModel.cost.cache_write ?? model.cost.cache.write,
},
experimentalOver200K: configModel.cost.context_over_200k
? {
input: configModel.cost.context_over_200k.input,
output: configModel.cost.context_over_200k.output,
cache: {
read: conf

packages/opencode/test/provider/provider.test.ts

+590
@@ -1213,6 +1213,65 @@ test("model cost overrides existing cost values", async () => {
})
})
test("OPENCODE_CONFIG_CONTENT overrides cost for configured model", async () => {
const overrideCost = {
input: 9.876543,
output: 21.234567,
cache_read: 3.210987,
cache_write: 4.109876,
}
const original = process.env["OPENCODE_CONFIG_CONTENT"]
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
}),
)
},
})
try {
process.env["OPENCODE_CONFIG_CONTENT"] = JSON.stringify({
$schema: "https://opencode.ai/config.json",
provider: {
"fictional-provider": {
npm: "@ai-sdk/openai-compatible",
env: [],
models: {
"fictional-model": {
cost: overrideCost,
},
},
options: {
apiKey: "test-api-key",
},
},
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const providers = a