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
2 changes: 1 addition & 1 deletion packages/types/src/__tests__/io-intelligence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe("IO Intelligence shared contract", () => {
it("registers the stable dynamic-provider identity and default model", () => {
expect(providerIdentifiers.ioIntelligence).toBe("io-intelligence")
expect(dynamicProviders).toContain(providerIdentifiers.ioIntelligence)
expect(getProviderDefaultModelId(providerIdentifiers.ioIntelligence)).toBe(ioIntelligenceDefaultModelId)
expect(getProviderDefaultModelId(providerIdentifiers.ioIntelligence, {})).toBe(ioIntelligenceDefaultModelId)
})

it("classifies the API key as secret and resolves the configured model", () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/__tests__/nanogpt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe("NanoGPT shared contract", () => {
it("registers the stable dynamic-provider identity and default model", () => {
expect(providerIdentifiers.nanogpt).toBe("nanogpt")
expect(dynamicProviders).toContain(providerIdentifiers.nanogpt)
expect(getProviderDefaultModelId(providerIdentifiers.nanogpt)).toBe(nanoGptDefaultModelId)
expect(getProviderDefaultModelId(providerIdentifiers.nanogpt, {})).toBe(nanoGptDefaultModelId)
})

it("classifies the API key as secret and resolves missing routing to auto", () => {
Expand Down
56 changes: 43 additions & 13 deletions packages/types/src/__tests__/provider-default-model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ vi.mock("../provider-identifiers.js", async (importOriginal) => {
import { providerIdentifiers } from "../provider-identifiers.js"
import {
anthropicDefaultModelId,
getProviderDefaultModelId,
getZAiDefaultModelId,
internationalZAiDefaultModelId,
kimiCodeDefaultModelId,
mainlandZAiDefaultModelId,
Expand All @@ -24,49 +24,79 @@ import {
vscodeLlmDefaultModelId,
zooGatewayDefaultModelId,
} from "../providers/index.js"
import { getProviderDefaultModelId } from "../provider-settings.js"

const zaiApiLineCases = [
["china_coding", "china"],
["china_api", "china"],
["international_coding", "international"],
["international_api", "international"],
] as const

describe("getProviderDefaultModelId", () => {
it("selects a static default through the canonical provider identifier", () => {
expect(getProviderDefaultModelId(providerIdentifiers.openrouter as ProviderName)).toBe(openRouterDefaultModelId)
expect(getProviderDefaultModelId(providerIdentifiers.openrouter as ProviderName, {})).toBe(
openRouterDefaultModelId,
)
})

it("triangulates static selection with another provider category", () => {
expect(getProviderDefaultModelId(providerIdentifiers.vscodeLm)).toBe(vscodeLlmDefaultModelId)
expect(getProviderDefaultModelId(providerIdentifiers.vscodeLm, {})).toBe(vscodeLlmDefaultModelId)
})

it("gives GitHub Copilot the same default as the VS Code LM provider", () => {
expect(getProviderDefaultModelId(providerIdentifiers.githubCopilot)).toBe(vscodeLlmDefaultModelId)
expect(getProviderDefaultModelId(providerIdentifiers.githubCopilot, {})).toBe(vscodeLlmDefaultModelId)
})

it("uses the canonical OpenAI Native default model", () => {
expect(getProviderDefaultModelId(providerIdentifiers.openaiNative)).toBe(openAiNativeDefaultModelId)
expect(getProviderDefaultModelId(providerIdentifiers.openaiNative, {})).toBe(openAiNativeDefaultModelId)
})

it.each([
[providerIdentifiers.kimiCode, kimiCodeDefaultModelId],
[providerIdentifiers.zooGateway, zooGatewayDefaultModelId],
])("preserves the %s default added on main", (provider, expectedModelId) => {
expect(getProviderDefaultModelId(provider)).toBe(expectedModelId)
expect(getProviderDefaultModelId(provider, {})).toBe(expectedModelId)
})

it.each(zaiApiLineCases)("resolves the %s Z.ai default through the provider definition", (zaiApiLine, region) => {
const expected = region === "china" ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId

expect(getProviderDefaultModelId(providerIdentifiers.zai, { zaiApiLine })).toBe(expected)
})

it("preserves region-dependent defaults", () => {
// These defaults currently share the same model ID, so the assertions document
// both branches but cannot detect swapped ternary arms until the IDs diverge.
expect(getProviderDefaultModelId(providerIdentifiers.zai, { isChina: true })).toBe(mainlandZAiDefaultModelId)
expect(getProviderDefaultModelId(providerIdentifiers.zai)).toBe(internationalZAiDefaultModelId)
it("falls back to the international Z.ai default when the API line is unset", () => {
expect(getProviderDefaultModelId(providerIdentifiers.zai, {})).toBe(internationalZAiDefaultModelId)
})

describe("Z.ai region mapping", () => {
// Both regional catalogs currently share a default model ID, so inject
// distinct values to prove the mainland/international arms are not swapped.
const distinctDefaults = { china: "test-china-model", international: "test-international-model" }

it.each(zaiApiLineCases)("maps %s to the %s default", (zaiApiLine, region) => {
expect(getZAiDefaultModelId(zaiApiLine, distinctDefaults)).toBe(distinctDefaults[region])
})

it.each([undefined, null, "bogus"])(
"falls back to the international default for the invalid API line %s",
(zaiApiLine) => {
expect(getZAiDefaultModelId(zaiApiLine, distinctDefaults)).toBe(distinctDefaults.international)
},
)
})

it.each([providerIdentifiers.openai, providerIdentifiers.ollama, providerIdentifiers.lmstudio])(
"returns an empty default for custom or locally selected models from %s",
(provider) => {
expect(getProviderDefaultModelId(provider)).toBe("")
expect(getProviderDefaultModelId(provider, {})).toBe("")
},
)

it.each([providerIdentifiers.anthropic, providerIdentifiers.geminiCli, providerIdentifiers.fakeAi])(
"preserves the Anthropic fallback for %s",
(provider) => {
expect(getProviderDefaultModelId(provider)).toBe(anthropicDefaultModelId)
expect(getProviderDefaultModelId(provider, {})).toBe(anthropicDefaultModelId)
},
)
})
12 changes: 11 additions & 1 deletion packages/types/src/__tests__/provider-settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
providerSettingsSchemaDiscriminated,
} from "../provider-settings.js"
import { OpenAiCodexServiceTier, OpenAiServiceTier } from "../model.js"
import { providerDefinitionList } from "../provider-settings/index.js"
import { providerDefinitionList } from "../providers/index.js"

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

it.each(providerDefinitionList.map((definition) => definition.apiProvider))(
"exposes a default model accessor for %s",
(apiProvider) => {
const definition = providerDefinitionList.find((candidate) => candidate.apiProvider === apiProvider)

expect(typeof definition?.getDefaultModelId).toBe("function")
expect(typeof definition?.getDefaultModelId({})).toBe("string")
},
)

it.each(providerNames)("accepts the %s provider branch", (apiProvider) => {
expect(providerSettingsSchemaDiscriminated.safeParse({ apiProvider }).success).toBe(true)
})
Expand Down
15 changes: 12 additions & 3 deletions packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from "zod"

import { providerDefinitionList, type ProviderDefinition } from "./provider-settings/index.js"
import { API_PROVIDER_FIELD, SETTINGS_SHAPE_FIELD } from "./provider-settings/common.js"
import { providerDefinitionList, type ProviderDefinition } from "./providers/index.js"
import { API_PROVIDER_FIELD, SETTINGS_SHAPE_FIELD } from "./providers/common.js"
export {
DEFAULT_OPEN_AI_STRICT_TOOL_SCHEMAS,
OPEN_AI_CODEX_SERVICE_TIER_KEY,
Expand All @@ -14,7 +14,7 @@ export {
type NanoGptRoutingPreference,
zaiApiLineSchema,
type ZaiApiLine,
} from "./provider-settings/index.js"
} from "./providers/index.js"

import { codebaseIndexProviderSchema } from "./codebase-index.js"
import type { UnionToIntersection } from "./type-fu.js"
Expand All @@ -25,6 +25,7 @@ import {
type RetiredProviderIdentifier,
} from "./provider-identifiers.js"
import {
anthropicDefaultModelId,
anthropicModels,
basetenModels,
bedrockModels,
Expand Down Expand Up @@ -359,6 +360,14 @@ export function getModelId(settings: ProviderSettings): string | undefined {
return undefined
}

/**
* Get the provider's default model ID, ignoring any configured model selection.
* Used as a fallback while provider models are still loading.
*/
export function getProviderDefaultModelId(provider: ProviderName, settings: ProviderSettings): string {
return providerDefinitions[provider]?.getDefaultModelId(settings) ?? anthropicDefaultModelId
}

/**
* ANTHROPIC_STYLE_PROVIDERS
*/
Expand Down
22 changes: 0 additions & 22 deletions packages/types/src/provider-settings/anthropic.ts

This file was deleted.

19 changes: 0 additions & 19 deletions packages/types/src/provider-settings/baseten.ts

This file was deleted.

36 changes: 0 additions & 36 deletions packages/types/src/provider-settings/bedrock.ts

This file was deleted.

20 changes: 0 additions & 20 deletions packages/types/src/provider-settings/deepseek.ts

This file was deleted.

19 changes: 0 additions & 19 deletions packages/types/src/provider-settings/fireworks.ts

This file was deleted.

19 changes: 0 additions & 19 deletions packages/types/src/provider-settings/friendli.ts

This file was deleted.

20 changes: 0 additions & 20 deletions packages/types/src/provider-settings/gemini.ts

This file was deleted.

Loading
Loading