Description
getModelCost() in models.config.mjs:143-157 unconditionally prefers costs from the user's existing config file over hardcoded SUPPORTED_MODELS pricing:
export function getModelCost(displayName, existingCosts) {
// Always prefer existing costs from config (user's custom values)
if (existingCosts && existingCosts[displayName]) {
return existingCosts[displayName]; // <-- always wins
}
// Fall back to hardcoded defaults if available
const config = getModelConfig(displayName);
if (config?.cost) {
return clone(config.cost);
}
return clone(DEFAULT_FALLBACK_COSTS);
}
Scenario
- User runs
opencode-nexos-models-config v1.17.0 → costs for "Claude Sonnet 4.5" are written to opencode.json as { input: 3.3, output: 16.5, ... }.
- Anthropic changes pricing. Package v1.18.0 updates
SUPPORTED_MODELS["Claude Sonnet 4.5"].cost to { input: 3.0, output: 15.0, ... }.
- User upgrades to v1.18.0 and re-runs the tool.
- Bug:
getExistingModelCosts() loads the old { input: 3.3, output: 16.5 } from the config file. getModelCost() returns these stale values. The updated pricing from v1.18.0 is silently ignored.
Impact
- Users who previously ran the tool never get pricing updates unless they manually delete their config or use
--custom-costs to override.
- No warning is shown that existing costs differ from the package's built-in pricing.
- This defeats the purpose of updating
SUPPORTED_MODELS costs in new releases.
Distinction: custom vs. stale costs
The current logic cannot distinguish between:
- User-customized costs (set via
--custom-costs) — should be preserved.
- Auto-generated costs from a previous run — should be updated.
Proposed fix
Option A: Always use hardcoded costs for supported models, only use existing costs for unsupported models:
export function getModelCost(displayName, existingCosts) {
const config = getModelConfig(displayName);
if (config?.cost) {
return clone(config.cost); // hardcoded always wins for supported models
}
if (existingCosts && existingCosts[displayName]) {
return existingCosts[displayName];
}
return clone(DEFAULT_FALLBACK_COSTS);
}
Option B: Track custom vs. auto-generated with a flag (e.g. "_customCost": true) and only preserve flagged entries.
Option C: Warn the user when existing costs differ from built-in pricing and ask whether to update.
Acceptance criteria
Test cases
test("should use updated hardcoded cost over stale existing cost for supported models", () => {
// Simulate: existing config has old pricing
const existingCosts = {
"Claude Sonnet 4.5": { input: 99.99, output: 99.99, cache_read: 99.99 }
};
const cost = getModelCost("Claude Sonnet 4.5", existingCosts);
// Should return the hardcoded SUPPORTED_MODELS cost, not the stale one
expect(cost).toEqual(SUPPORTED_MODELS["Claude Sonnet 4.5"].cost);
expect(cost.input).not.toBe(99.99);
});
test("should preserve existing costs for unsupported models", () => {
const existingCosts = {
"Custom Model": { input: 10, output: 20 }
};
const cost = getModelCost("Custom Model", existingCosts);
expect(cost).toEqual({ input: 10, output: 20 });
});
test("should preserve user custom costs set via --custom-costs", () => {
// This test depends on the chosen implementation approach
// For Option B: check that _customCost flag is respected
});
🤖 Generated with Claude Code
Description
getModelCost()inmodels.config.mjs:143-157unconditionally prefers costs from the user's existing config file over hardcodedSUPPORTED_MODELSpricing:Scenario
opencode-nexos-models-configv1.17.0 → costs for "Claude Sonnet 4.5" are written toopencode.jsonas{ input: 3.3, output: 16.5, ... }.SUPPORTED_MODELS["Claude Sonnet 4.5"].costto{ input: 3.0, output: 15.0, ... }.getExistingModelCosts()loads the old{ input: 3.3, output: 16.5 }from the config file.getModelCost()returns these stale values. The updated pricing from v1.18.0 is silently ignored.Impact
--custom-coststo override.SUPPORTED_MODELScosts in new releases.Distinction: custom vs. stale costs
The current logic cannot distinguish between:
--custom-costs) — should be preserved.Proposed fix
Option A: Always use hardcoded costs for supported models, only use existing costs for unsupported models:
Option B: Track custom vs. auto-generated with a flag (e.g.
"_customCost": true) and only preserve flagged entries.Option C: Warn the user when existing costs differ from built-in pricing and ask whether to update.
Acceptance criteria
SUPPORTED_MODELS, re-running the tool produces the new pricing in the config file.--custom-costs) are still preserved.Test cases
🤖 Generated with Claude Code