Skip to content
Merged
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
23 changes: 23 additions & 0 deletions src/utils/model/model.openai-shim-providers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,29 @@ test('getDefaultMainLoopModelSetting defaults MiniMax to M3', async () => {
expect(getDefaultMainLoopModel()).toBe('MiniMax-M3')
})

test('getDefaultMainLoopModelSetting uses the NVIDIA NIM route model', async () => {
process.env.NVIDIA_NIM = '1'
process.env.CLAUDE_CODE_USE_OPENAI = '1'
process.env.OPENAI_MODEL = 'meta/llama-3.3-70b-instruct'

const {
getDefaultMainLoopModel,
getDefaultMainLoopModelSetting,
} = await importFreshModelModule()
expect(getDefaultMainLoopModelSetting()).toBe('meta/llama-3.3-70b-instruct')
expect(getDefaultMainLoopModel()).toBe('meta/llama-3.3-70b-instruct')
})

test('getDefaultMainLoopModelSetting falls back to the NVIDIA NIM descriptor default', async () => {
process.env.NVIDIA_NIM = '1'
process.env.CLAUDE_CODE_USE_OPENAI = '1'

const { getDefaultMainLoopModelSetting } = await importFreshModelModule()
expect(getDefaultMainLoopModelSetting()).toBe(
'nvidia/llama-3.1-nemotron-70b-instruct',
)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

test('getDefaultMainLoopModelSetting defaults Xiaomi MiMo to mimo-v2.5-pro', async () => {
process.env.MIMO_API_KEY = 'mimo-test'
process.env.CLAUDE_CODE_USE_OPENAI = '1'
Expand Down
10 changes: 10 additions & 0 deletions src/utils/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { type ModelAlias, isModelAlias } from './aliases.js'
import { capitalize } from '../stringUtils.js'
import { DEFAULT_GEMINI_MODEL } from '../providerProfile.js'
import { getAntModelOverrideConfig, resolveAntModel } from './antModels.js'
import { getRouteDefaultModel } from '../../integrations/routeMetadata.js'

export type ModelShortName = string
export type ModelName = string
Expand Down Expand Up @@ -388,6 +389,15 @@ export function getDefaultMainLoopModelSetting(): ModelName | ModelAlias {
if (getAPIProvider() === 'codex') {
return process.env.OPENAI_MODEL || 'gpt-5.5'
}
// NVIDIA NIM uses OpenAI-compatible model ids. Keep this fallback aligned
// with the route descriptor so headless sessions never send a Claude model.
if (getAPIProvider() === 'nvidia-nim') {
return (
process.env.OPENAI_MODEL ||
getRouteDefaultModel('nvidia-nim') ||
'nvidia/llama-3.1-nemotron-70b-instruct'
)
}
// xAI provider: always use the configured Grok model (default grok-4.3)
if (getAPIProvider() === 'xai') {
return process.env.OPENAI_MODEL || 'grok-4.3'
Expand Down
23 changes: 23 additions & 0 deletions src/utils/modelCost.modelGate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,29 @@ afterEach(() => {
}
})

test('unknown models do not inherit the configured default model price', async () => {
mock.module('./model/model.js', () => ({
firstPartyNameToCanonical: (model: string) => {
if (model.includes('claude-haiku-4-5')) return 'claude-haiku-4-5'
return model
},
getCanonicalName: (model: string) => {
if (model.includes('claude-haiku-4-5')) return 'claude-haiku-4-5'
return model
},
getDefaultMainLoopModelSetting: () => 'claude-haiku-4-5',
}))
const { getModelCosts, COST_HAIKU_45, COST_TIER_5_25 } =
await importFreshModelCost()

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const usage = {} as any
const costs = getModelCosts('meta/llama-3.3-70b-instruct', usage)

expect(costs).toEqual(COST_TIER_5_25)
expect(costs).not.toEqual(COST_HAIKU_45)
})

// Regression for #1769: fast mode is now enabled for Opus 4.8, but getModelCosts
// only applied the elevated fast-mode tier to opus-4-6, so fast-mode 4.8 was
// billed at the normal rate while the picker advertised the fast-mode price.
Expand Down
9 changes: 3 additions & 6 deletions src/utils/modelCost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
import {
firstPartyNameToCanonical,
getCanonicalName,
getDefaultMainLoopModelSetting,
type ModelShortName,
} from './model/model.js'

Expand Down Expand Up @@ -166,10 +165,7 @@ export function getModelCosts(model: string, usage: Usage): ModelCosts {
const costs = MODEL_COSTS[shortName]
if (!costs) {
trackUnknownModelCost(model, shortName)
return (
MODEL_COSTS[getCanonicalName(getDefaultMainLoopModelSetting())] ??
DEFAULT_UNKNOWN_MODEL_COST
)
return DEFAULT_UNKNOWN_MODEL_COST
}
return costs
}
Expand All @@ -184,7 +180,8 @@ function trackUnknownModelCost(model: string, shortName: ModelShortName): void {
}

// Calculate the cost of a query in US dollars.
// If the model's costs are not found, use the default model's costs.
// Unknown models use the explicit unknown-model estimate and are marked in
// session state; they must never inherit an unrelated configured default.
export function calculateUSDCost(resolvedModel: string, usage: Usage): number {
const modelCosts = getModelCosts(resolvedModel, usage)
return tokensToUSDCost(modelCosts, usage)
Expand Down