Skip to content

Commit 0001e98

Browse files
committed
merge: re-merge onto parent #2264923478c (main@070ee98eb)
Brings the leaf's settings/UI prompt-cache-TTL surface onto the parent's re-anchored provider layout (#2233 moved provider-settings/ to providers/ and added the TTL knob to baseProviderSettingsShape). No textual conflict: the two sides touch disjoint paths. The leaf's delta is webview/settings/UI only (Anthropic.tsx control, i18n, SettingsView and ClineProvider specs); the parent's delta is packages/types + provider runtime. packages/types/src/providers/common.ts resolves byte-identical to the parent, which already carries both #2233's structure and the TTL knob. Verified: 32 co-touched specs run by name (1178 tests), check-types green for packages/types, src and webview-ui, eslint --max-warnings=0 on all 97 touched files, no stale provider-settings/ imports, eslint-suppressions.json unchanged.
2 parents 064dfb8 + 923478c commit 0001e98

126 files changed

Lines changed: 2755 additions & 1584 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎packages/types/src/__tests__/io-intelligence.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe("IO Intelligence shared contract", () => {
1212
it("registers the stable dynamic-provider identity and default model", () => {
1313
expect(providerIdentifiers.ioIntelligence).toBe("io-intelligence")
1414
expect(dynamicProviders).toContain(providerIdentifiers.ioIntelligence)
15-
expect(getProviderDefaultModelId(providerIdentifiers.ioIntelligence)).toBe(ioIntelligenceDefaultModelId)
15+
expect(getProviderDefaultModelId(providerIdentifiers.ioIntelligence, {})).toBe(ioIntelligenceDefaultModelId)
1616
})
1717

1818
it("classifies the API key as secret and resolves the configured model", () => {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { ModelInfo } from "../model.js"
2+
import { mistralModels } from "../providers/mistral.js"
3+
4+
const modelEntries: [string, ModelInfo][] = Object.entries(mistralModels)
5+
6+
describe("mistral registry cache pricing", () => {
7+
it("covers all nine catalog entries", () => {
8+
expect(modelEntries).toHaveLength(9)
9+
})
10+
11+
it.each(modelEntries)("%s bills cache reads at 10%% of its input price", (id, info) => {
12+
expect(id).toBeTruthy()
13+
expect(info.inputPrice).toBeGreaterThan(0)
14+
expect(info.cacheReadsPrice).toBeDefined()
15+
// Mistral bills cached prompt tokens at 10% of the standard input price.
16+
expect(info.cacheReadsPrice).toBeCloseTo((info.inputPrice as number) * 0.1, 10)
17+
})
18+
19+
it.each(modelEntries)("%s keeps the request-side cache marker off", (id, info) => {
20+
// Mistral takes no cache_control markers in the request body, so the flag
21+
// that gates marker insertion stays false even though reads are reported.
22+
expect(id).toBeTruthy()
23+
expect(info.supportsPromptCache).toBe(false)
24+
})
25+
})

‎packages/types/src/__tests__/nanogpt.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe("NanoGPT shared contract", () => {
1414
it("registers the stable dynamic-provider identity and default model", () => {
1515
expect(providerIdentifiers.nanogpt).toBe("nanogpt")
1616
expect(dynamicProviders).toContain(providerIdentifiers.nanogpt)
17-
expect(getProviderDefaultModelId(providerIdentifiers.nanogpt)).toBe(nanoGptDefaultModelId)
17+
expect(getProviderDefaultModelId(providerIdentifiers.nanogpt, {})).toBe(nanoGptDefaultModelId)
1818
})
1919

2020
it("classifies the API key as secret and resolves missing routing to auto", () => {

‎packages/types/src/__tests__/opencode-go.test.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ describe("opencode-go registry", () => {
3434
"kimi-k2.6",
3535
"mimo-v2.5",
3636
"mimo-v2.5-pro",
37+
"mimo-v2.6-flash",
38+
"mimo-v2.6-pro",
3739
"deepseek-v4-pro",
3840
"deepseek-v4-flash",
3941
"deepseek-v4.1-flash",
@@ -361,6 +363,32 @@ describe("opencode-go registry", () => {
361363
cacheReadsPrice: 0.08,
362364
},
363365
},
366+
{
367+
id: "mimo-v2.6-flash",
368+
expected: {
369+
maxTokens: 131_072,
370+
contextWindow: 1_048_576,
371+
supportsImages: true,
372+
supportsPromptCache: false,
373+
preserveReasoning: true,
374+
inputPrice: 0.14,
375+
outputPrice: 0.28,
376+
cacheReadsPrice: 0.0028,
377+
},
378+
},
379+
{
380+
id: "mimo-v2.6-pro",
381+
expected: {
382+
maxTokens: 131_072,
383+
contextWindow: 1_048_576,
384+
supportsImages: true,
385+
supportsPromptCache: false,
386+
preserveReasoning: true,
387+
inputPrice: 0.435,
388+
outputPrice: 0.87,
389+
cacheReadsPrice: 0.003625,
390+
},
391+
},
364392
{
365393
id: "qwen3.5-plus",
366394
expected: {

‎packages/types/src/__tests__/prompt-cache-ttl.test.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { describe, it, expect } from "vitest"
44

5-
import { anthropicModels } from "../providers/anthropic.js"
5+
import { anthropicModels, anthropicProviderDefinition } from "../providers/anthropic.js"
66
import { vertexModels } from "../providers/vertex.js"
77
import { openRouterDefaultModelInfo } from "../providers/openrouter.js"
88
import {
@@ -16,8 +16,7 @@ import {
1616
promptCacheTtlValues,
1717
type ModelInfo,
1818
} from "../model.js"
19-
import { anthropicProviderDefinition } from "../provider-settings/anthropic.js"
20-
import { baseProviderSettingsShape } from "../provider-settings/common.js"
19+
import { baseProviderSettingsShape } from "../providers/common.js"
2120
import { providerIdentifiers } from "../provider-identifiers.js"
2221
import { globalSettingsSchema } from "../global-settings.js"
2322

‎packages/types/src/__tests__/provider-default-model.test.ts‎

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ vi.mock("../provider-identifiers.js", async (importOriginal) => {
1515
import { providerIdentifiers } from "../provider-identifiers.js"
1616
import {
1717
anthropicDefaultModelId,
18-
getProviderDefaultModelId,
18+
getZAiDefaultModelId,
1919
internationalZAiDefaultModelId,
2020
kimiCodeDefaultModelId,
2121
mainlandZAiDefaultModelId,
@@ -24,49 +24,79 @@ import {
2424
vscodeLlmDefaultModelId,
2525
zooGatewayDefaultModelId,
2626
} from "../providers/index.js"
27+
import { getProviderDefaultModelId } from "../provider-settings.js"
28+
29+
const zaiApiLineCases = [
30+
["china_coding", "china"],
31+
["china_api", "china"],
32+
["international_coding", "international"],
33+
["international_api", "international"],
34+
] as const
2735

2836
describe("getProviderDefaultModelId", () => {
2937
it("selects a static default through the canonical provider identifier", () => {
30-
expect(getProviderDefaultModelId(providerIdentifiers.openrouter as ProviderName)).toBe(openRouterDefaultModelId)
38+
expect(getProviderDefaultModelId(providerIdentifiers.openrouter as ProviderName, {})).toBe(
39+
openRouterDefaultModelId,
40+
)
3141
})
3242

3343
it("triangulates static selection with another provider category", () => {
34-
expect(getProviderDefaultModelId(providerIdentifiers.vscodeLm)).toBe(vscodeLlmDefaultModelId)
44+
expect(getProviderDefaultModelId(providerIdentifiers.vscodeLm, {})).toBe(vscodeLlmDefaultModelId)
3545
})
3646

3747
it("gives GitHub Copilot the same default as the VS Code LM provider", () => {
38-
expect(getProviderDefaultModelId(providerIdentifiers.githubCopilot)).toBe(vscodeLlmDefaultModelId)
48+
expect(getProviderDefaultModelId(providerIdentifiers.githubCopilot, {})).toBe(vscodeLlmDefaultModelId)
3949
})
4050

4151
it("uses the canonical OpenAI Native default model", () => {
42-
expect(getProviderDefaultModelId(providerIdentifiers.openaiNative)).toBe(openAiNativeDefaultModelId)
52+
expect(getProviderDefaultModelId(providerIdentifiers.openaiNative, {})).toBe(openAiNativeDefaultModelId)
4353
})
4454

4555
it.each([
4656
[providerIdentifiers.kimiCode, kimiCodeDefaultModelId],
4757
[providerIdentifiers.zooGateway, zooGatewayDefaultModelId],
4858
])("preserves the %s default added on main", (provider, expectedModelId) => {
49-
expect(getProviderDefaultModelId(provider)).toBe(expectedModelId)
59+
expect(getProviderDefaultModelId(provider, {})).toBe(expectedModelId)
60+
})
61+
62+
it.each(zaiApiLineCases)("resolves the %s Z.ai default through the provider definition", (zaiApiLine, region) => {
63+
const expected = region === "china" ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId
64+
65+
expect(getProviderDefaultModelId(providerIdentifiers.zai, { zaiApiLine })).toBe(expected)
5066
})
5167

52-
it("preserves region-dependent defaults", () => {
53-
// These defaults currently share the same model ID, so the assertions document
54-
// both branches but cannot detect swapped ternary arms until the IDs diverge.
55-
expect(getProviderDefaultModelId(providerIdentifiers.zai, { isChina: true })).toBe(mainlandZAiDefaultModelId)
56-
expect(getProviderDefaultModelId(providerIdentifiers.zai)).toBe(internationalZAiDefaultModelId)
68+
it("falls back to the international Z.ai default when the API line is unset", () => {
69+
expect(getProviderDefaultModelId(providerIdentifiers.zai, {})).toBe(internationalZAiDefaultModelId)
70+
})
71+
72+
describe("Z.ai region mapping", () => {
73+
// Both regional catalogs currently share a default model ID, so inject
74+
// distinct values to prove the mainland/international arms are not swapped.
75+
const distinctDefaults = { china: "test-china-model", international: "test-international-model" }
76+
77+
it.each(zaiApiLineCases)("maps %s to the %s default", (zaiApiLine, region) => {
78+
expect(getZAiDefaultModelId(zaiApiLine, distinctDefaults)).toBe(distinctDefaults[region])
79+
})
80+
81+
it.each([undefined, null, "bogus"])(
82+
"falls back to the international default for the invalid API line %s",
83+
(zaiApiLine) => {
84+
expect(getZAiDefaultModelId(zaiApiLine, distinctDefaults)).toBe(distinctDefaults.international)
85+
},
86+
)
5787
})
5888

5989
it.each([providerIdentifiers.openai, providerIdentifiers.ollama, providerIdentifiers.lmstudio])(
6090
"returns an empty default for custom or locally selected models from %s",
6191
(provider) => {
62-
expect(getProviderDefaultModelId(provider)).toBe("")
92+
expect(getProviderDefaultModelId(provider, {})).toBe("")
6393
},
6494
)
6595

6696
it.each([providerIdentifiers.anthropic, providerIdentifiers.geminiCli, providerIdentifiers.fakeAi])(
6797
"preserves the Anthropic fallback for %s",
6898
(provider) => {
69-
expect(getProviderDefaultModelId(provider)).toBe(anthropicDefaultModelId)
99+
expect(getProviderDefaultModelId(provider, {})).toBe(anthropicDefaultModelId)
70100
},
71101
)
72102
})

‎packages/types/src/__tests__/provider-settings.test.ts‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
providerSettingsSchemaDiscriminated,
99
} from "../provider-settings.js"
1010
import { OpenAiCodexServiceTier, OpenAiServiceTier } from "../model.js"
11-
import { providerDefinitionList } from "../provider-settings/index.js"
11+
import { providerDefinitionList } from "../providers/index.js"
1212

1313
describe("provider settings discriminated union", () => {
1414
it("composes exactly one provider-specific definition for every provider", () => {
@@ -18,6 +18,16 @@ describe("provider settings discriminated union", () => {
1818
expect(new Set(registeredProviders).size).toBe(providerNames.length)
1919
})
2020

21+
it.each(providerDefinitionList.map((definition) => definition.apiProvider))(
22+
"exposes a default model accessor for %s",
23+
(apiProvider) => {
24+
const definition = providerDefinitionList.find((candidate) => candidate.apiProvider === apiProvider)
25+
26+
expect(typeof definition?.getDefaultModelId).toBe("function")
27+
expect(typeof definition?.getDefaultModelId({})).toBe("string")
28+
},
29+
)
30+
2131
it.each(providerNames)("accepts the %s provider branch", (apiProvider) => {
2232
expect(providerSettingsSchemaDiscriminated.safeParse({ apiProvider }).success).toBe(true)
2333
})

‎packages/types/src/provider-settings.ts‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod"
22

3-
import { providerDefinitionList, type ProviderDefinition } from "./provider-settings/index.js"
4-
import { API_PROVIDER_FIELD, SETTINGS_SHAPE_FIELD } from "./provider-settings/common.js"
3+
import { providerDefinitionList, type ProviderDefinition } from "./providers/index.js"
4+
import { API_PROVIDER_FIELD, SETTINGS_SHAPE_FIELD } from "./providers/common.js"
55
export {
66
DEFAULT_OPEN_AI_STRICT_TOOL_SCHEMAS,
77
OPEN_AI_CODEX_SERVICE_TIER_KEY,
@@ -14,7 +14,7 @@ export {
1414
type NanoGptRoutingPreference,
1515
zaiApiLineSchema,
1616
type ZaiApiLine,
17-
} from "./provider-settings/index.js"
17+
} from "./providers/index.js"
1818

1919
import { codebaseIndexProviderSchema } from "./codebase-index.js"
2020
import type { UnionToIntersection } from "./type-fu.js"
@@ -25,6 +25,7 @@ import {
2525
type RetiredProviderIdentifier,
2626
} from "./provider-identifiers.js"
2727
import {
28+
anthropicDefaultModelId,
2829
anthropicModels,
2930
basetenModels,
3031
bedrockModels,
@@ -359,6 +360,14 @@ export function getModelId(settings: ProviderSettings): string | undefined {
359360
return undefined
360361
}
361362

363+
/**
364+
* Get the provider's default model ID, ignoring any configured model selection.
365+
* Used as a fallback while provider models are still loading.
366+
*/
367+
export function getProviderDefaultModelId(provider: ProviderName, settings: ProviderSettings): string {
368+
return providerDefinitions[provider]?.getDefaultModelId(settings) ?? anthropicDefaultModelId
369+
}
370+
362371
/**
363372
* ANTHROPIC_STYLE_PROVIDERS
364373
*/

‎packages/types/src/provider-settings/anthropic.ts‎

Lines changed: 0 additions & 22 deletions
This file was deleted.

‎packages/types/src/provider-settings/baseten.ts‎

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)