From 9beac129db034170bdee904afecba5b3fed8d41f Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 01:21:22 +0300 Subject: [PATCH 01/16] refactor(types): centralize provider model definitions --- .../src/__tests__/provider-model-id.test.ts | 58 ++ .../src/__tests__/provider-settings.test.ts | 8 +- packages/types/src/provider-settings.ts | 762 +++++++++++------- packages/types/src/type-fu.ts | 4 + webview-ui/src/utils/validate.ts | 28 +- 5 files changed, 537 insertions(+), 323 deletions(-) create mode 100644 packages/types/src/__tests__/provider-model-id.test.ts diff --git a/packages/types/src/__tests__/provider-model-id.test.ts b/packages/types/src/__tests__/provider-model-id.test.ts new file mode 100644 index 0000000000..c9ac8e869c --- /dev/null +++ b/packages/types/src/__tests__/provider-model-id.test.ts @@ -0,0 +1,58 @@ +import { getModelId, providerIdentifiers, type ProviderSettings } from "../index.js" + +describe("getModelId", () => { + it("uses the provider-specific model ID field", () => { + const settings: ProviderSettings = { + apiProvider: providerIdentifiers.openrouter, + apiModelId: "unrelated-model", + openRouterModelId: "openrouter-model", + } + + expect(getModelId(settings)).toBe("openrouter-model") + }) + + it("uses the active provider when other model ID fields are present", () => { + const settings: ProviderSettings = { + apiProvider: providerIdentifiers.ollama, + apiModelId: "anthropic-model", + ollamaModelId: "ollama-model", + } + + expect(getModelId(settings)).toBe("ollama-model") + }) + + it("uses the nested model selector for VS Code LM", () => { + const settings: ProviderSettings = { + apiProvider: providerIdentifiers.vscodeLm, + vsCodeLmModelSelector: { vendor: "copilot", family: "gpt-4o", id: "vscode-model", version: "1" }, + } + + expect(getModelId(settings)).toBe("vscode-model") + }) + + it("uses openAiModelId for OpenAI Compatible", () => { + const settings: ProviderSettings = { + apiProvider: providerIdentifiers.openai, + apiModelId: "unrelated-model", + openAiModelId: "openai-compatible-model", + } + + expect(getModelId(settings)).toBe("openai-compatible-model") + }) + + it.each([providerIdentifiers.openaiNative, providerIdentifiers.fakeAi])("uses apiModelId for %s", (apiProvider) => { + const settings: ProviderSettings = { apiProvider, apiModelId: "shared-model" } + + expect(getModelId(settings)).toBe("shared-model") + }) + + it("returns undefined when no provider is selected", () => { + expect(getModelId({})).toBeUndefined() + }) + + it("resolves a model ID for every provider definition without throwing", () => { + for (const apiProvider of Object.values(providerIdentifiers)) { + expect(() => getModelId({ apiProvider })).not.toThrow() + } + }) +}) diff --git a/packages/types/src/__tests__/provider-settings.test.ts b/packages/types/src/__tests__/provider-settings.test.ts index cd786a6529..98471809b3 100644 --- a/packages/types/src/__tests__/provider-settings.test.ts +++ b/packages/types/src/__tests__/provider-settings.test.ts @@ -1,4 +1,4 @@ -import { ANTHROPIC_API_PROTOCOL, OPENAI_API_PROTOCOL, providerIdentifiers } from "../index.js" +import { ANTHROPIC_API_PROTOCOL, OPENAI_API_PROTOCOL, providerIdentifiers, providerNames } from "../index.js" import { getApiProtocol, OPEN_AI_CODEX_SERVICE_TIER_KEY, @@ -8,6 +8,12 @@ import { } from "../provider-settings.js" import { OpenAiCodexServiceTier, OpenAiServiceTier } from "../model.js" +describe("provider settings discriminated union", () => { + it.each(providerNames)("accepts the %s provider branch", (apiProvider) => { + expect(providerSettingsSchemaDiscriminated.safeParse({ apiProvider }).success).toBe(true) + }) +}) + describe("OpenAI Codex provider settings", () => { it("preserves the Fast preference in general and provider-specific schemas", () => { const settings = { diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 99b75de2e4..1e4b8cc1e1 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -8,6 +8,7 @@ import { serviceTierSchema, } from "./model.js" import { codebaseIndexProviderSchema } from "./codebase-index.js" +import type { UnionToIntersection } from "./type-fu.js" import { providerIdentifiers, retiredProviderIdentifiers, @@ -160,10 +161,13 @@ export type ProviderNameWithRetired = z.infer * ProviderSettings */ -const baseProviderSettingsSchema = z.object({ +const baseProviderSettingsShape = { includeMaxTokens: z.boolean().optional(), todoListEnabled: z.boolean().optional(), modelTemperature: z.number().nullish(), @@ -188,319 +192,543 @@ const baseProviderSettingsSchema = z.object({ // Model verbosity. verbosity: verbosityLevelsSchema.optional(), +} + +type ProviderModelIdAccessor = (settings: Record) => string | undefined + +type TypedProviderModelIdAccessor = (settings: z.infer>) => string | undefined + +const createProviderDefinition =

({ + apiProvider, + schema, + getModelId, +}: { + apiProvider: P + schema: S + getModelId: TypedProviderModelIdAccessor +}) => ({ + apiProvider, + settingsShape: schema, + schema: z.object({ + ...schema, + [API_PROVIDER_FIELD]: z.literal(apiProvider), + }), + getModelId: ((settings) => getModelId(settings as z.infer>)) satisfies ProviderModelIdAccessor, }) // Several of the providers share common model config properties. -const apiModelIdProviderModelSchema = baseProviderSettingsSchema.extend({ +const apiModelIdProviderModelShape = { + ...baseProviderSettingsShape, apiModelId: z.string().optional(), -}) +} -const anthropicSchema = apiModelIdProviderModelSchema.extend({ - apiKey: z.string().optional(), - anthropicBaseUrl: z.string().optional(), - anthropicUseAuthToken: z.boolean().optional(), - anthropicBeta1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. -}) +const anthropicProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.anthropic, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + apiKey: z.string().optional(), + anthropicBaseUrl: z.string().optional(), + anthropicUseAuthToken: z.boolean().optional(), + anthropicBeta1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + }, +}) -const openRouterSchema = baseProviderSettingsSchema.extend({ - openRouterApiKey: z.string().optional(), - openRouterModelId: z.string().optional(), - openRouterBaseUrl: z.string().optional(), - openRouterSpecificProvider: z.string().optional(), +const openRouterProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openrouter, + getModelId: (settings) => settings.openRouterModelId, + schema: { + ...baseProviderSettingsShape, + openRouterApiKey: z.string().optional(), + openRouterModelId: z.string().optional(), + openRouterBaseUrl: z.string().optional(), + openRouterSpecificProvider: z.string().optional(), + }, }) -const bedrockSchema = apiModelIdProviderModelSchema.extend({ - awsAccessKey: z.string().optional(), - awsSecretKey: z.string().optional(), - awsSessionToken: z.string().optional(), - awsRegion: z.string().optional(), - awsUseCrossRegionInference: z.boolean().optional(), - awsUseGlobalInference: z.boolean().optional(), // Enable Global Inference profile routing when supported - awsUsePromptCache: z.boolean().optional(), - awsProfile: z.string().optional(), - awsUseProfile: z.boolean().optional(), - awsApiKey: z.string().optional(), - awsUseApiKey: z.boolean().optional(), - awsCustomArn: z.string().optional(), - awsModelContextWindow: z.number().optional(), - awsBedrockEndpointEnabled: z.boolean().optional(), - awsBedrockEndpoint: z.string().optional(), - awsBedrock1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. - awsBedrockServiceTier: z.enum(["STANDARD", "FLEX", "PRIORITY"]).optional(), // AWS Bedrock service tier selection +const bedrockProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.bedrock, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + awsAccessKey: z.string().optional(), + awsSecretKey: z.string().optional(), + awsSessionToken: z.string().optional(), + awsRegion: z.string().optional(), + awsUseCrossRegionInference: z.boolean().optional(), + awsUseGlobalInference: z.boolean().optional(), // Enable Global Inference profile routing when supported + awsUsePromptCache: z.boolean().optional(), + awsProfile: z.string().optional(), + awsUseProfile: z.boolean().optional(), + awsApiKey: z.string().optional(), + awsUseApiKey: z.boolean().optional(), + awsCustomArn: z.string().optional(), + awsModelContextWindow: z.number().optional(), + awsBedrockEndpointEnabled: z.boolean().optional(), + awsBedrockEndpoint: z.string().optional(), + awsBedrock1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + awsBedrockServiceTier: z.enum(["STANDARD", "FLEX", "PRIORITY"]).optional(), // AWS Bedrock service tier selection + }, }) -const vertexSchema = apiModelIdProviderModelSchema.extend({ - vertexKeyFile: z.string().optional(), - vertexJsonCredentials: z.string().optional(), - vertexProjectId: z.string().optional(), - vertexRegion: z.string().optional(), - vertex1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. +const vertexProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.vertex, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + vertexKeyFile: z.string().optional(), + vertexJsonCredentials: z.string().optional(), + vertexProjectId: z.string().optional(), + vertexRegion: z.string().optional(), + vertex1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + }, }) -const openAiSchema = baseProviderSettingsSchema.extend({ - openAiBaseUrl: z.string().optional(), - openAiApiKey: z.string().optional(), - openAiR1FormatEnabled: z.boolean().optional(), - openAiModelId: z.string().optional(), - openAiCustomModelInfo: modelInfoSchema.nullish(), - openAiUseAzure: z.boolean().optional(), - azureApiVersion: z.string().optional(), - openAiStreamingEnabled: z.boolean().optional(), - openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. - openAiHeaders: z.record(z.string(), z.string()).optional(), +const openAiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openai, + getModelId: (settings) => settings.openAiModelId, + schema: { + ...baseProviderSettingsShape, + openAiBaseUrl: z.string().optional(), + openAiApiKey: z.string().optional(), + openAiR1FormatEnabled: z.boolean().optional(), + openAiModelId: z.string().optional(), + openAiCustomModelInfo: modelInfoSchema.nullish(), + openAiUseAzure: z.boolean().optional(), + azureApiVersion: z.string().optional(), + openAiStreamingEnabled: z.boolean().optional(), + openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. + openAiHeaders: z.record(z.string(), z.string()).optional(), + }, }) -const ollamaSchema = baseProviderSettingsSchema.extend({ - ollamaModelId: z.string().optional(), - ollamaBaseUrl: z.string().optional(), - ollamaApiKey: z.string().optional(), - ollamaNumCtx: z.number().int().min(128).optional(), +const ollamaProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.ollama, + getModelId: (settings) => settings.ollamaModelId, + schema: { + ...baseProviderSettingsShape, + ollamaModelId: z.string().optional(), + ollamaBaseUrl: z.string().optional(), + ollamaApiKey: z.string().optional(), + ollamaNumCtx: z.number().int().min(128).optional(), + }, }) -const vsCodeLmSchema = baseProviderSettingsSchema.extend({ - vsCodeLmModelSelector: z - .object({ - vendor: z.string().optional(), - family: z.string().optional(), - version: z.string().optional(), - id: z.string().optional(), - }) - .optional(), +const vsCodeLmProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.vscodeLm, + getModelId: (settings) => settings.vsCodeLmModelSelector?.id, + schema: { + ...baseProviderSettingsShape, + vsCodeLmModelSelector: z + .object({ + vendor: z.string().optional(), + family: z.string().optional(), + version: z.string().optional(), + id: z.string().optional(), + }) + .optional(), + }, }) -const lmStudioSchema = baseProviderSettingsSchema.extend({ - lmStudioModelId: z.string().optional(), - lmStudioBaseUrl: z.string().optional(), - lmStudioDraftModelId: z.string().optional(), - lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), +const lmStudioProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.lmstudio, + getModelId: (settings) => settings.lmStudioModelId, + schema: { + ...baseProviderSettingsShape, + lmStudioModelId: z.string().optional(), + lmStudioBaseUrl: z.string().optional(), + lmStudioDraftModelId: z.string().optional(), + lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), + }, }) -const geminiSchema = apiModelIdProviderModelSchema.extend({ - geminiApiKey: z.string().optional(), - googleGeminiBaseUrl: z.string().optional(), +const geminiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.gemini, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + geminiApiKey: z.string().optional(), + googleGeminiBaseUrl: z.string().optional(), + }, }) -const geminiCliSchema = apiModelIdProviderModelSchema.extend({ - geminiCliOAuthPath: z.string().optional(), - geminiCliProjectId: z.string().optional(), +const geminiCliProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.geminiCli, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + geminiCliOAuthPath: z.string().optional(), + geminiCliProjectId: z.string().optional(), + }, }) -const openAiCodexSchema = apiModelIdProviderModelSchema.extend({ - // Codex "Fast" mode maps to the Responses API priority service tier. - [OPEN_AI_CODEX_SERVICE_TIER_KEY]: openAiCodexServiceTierSchema.optional(), +const openAiCodexProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openaiCodex, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + // Codex "Fast" mode maps to the Responses API priority service tier. + [OPEN_AI_CODEX_SERVICE_TIER_KEY]: openAiCodexServiceTierSchema.optional(), + }, }) -const openAiNativeSchema = apiModelIdProviderModelSchema.extend({ - openAiNativeApiKey: z.string().optional(), - openAiNativeBaseUrl: z.string().optional(), - // OpenAI Responses API service tier for openai-native provider only. - // UI should only expose this when the selected model supports flex/priority. - openAiNativeServiceTier: serviceTierSchema.optional(), +const openAiNativeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openaiNative, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + openAiNativeApiKey: z.string().optional(), + openAiNativeBaseUrl: z.string().optional(), + // OpenAI Responses API service tier for openai-native provider only. + // UI should only expose this when the selected model supports flex/priority. + openAiNativeServiceTier: serviceTierSchema.optional(), + }, }) -const mistralSchema = apiModelIdProviderModelSchema.extend({ - mistralApiKey: z.string().optional(), - mistralCodestralUrl: z.string().optional(), +const mistralProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.mistral, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + mistralApiKey: z.string().optional(), + mistralCodestralUrl: z.string().optional(), + }, }) -const deepSeekSchema = apiModelIdProviderModelSchema.extend({ - deepSeekBaseUrl: z.string().optional(), - deepSeekApiKey: z.string().optional(), +const deepSeekProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.deepseek, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + deepSeekBaseUrl: z.string().optional(), + deepSeekApiKey: z.string().optional(), + }, }) -const poeSchema = apiModelIdProviderModelSchema.extend({ - poeApiKey: z.string().optional(), - poeBaseUrl: z.string().optional(), +const poeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.poe, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + poeApiKey: z.string().optional(), + poeBaseUrl: z.string().optional(), + }, }) -const moonshotSchema = apiModelIdProviderModelSchema.extend({ - moonshotBaseUrl: z - .union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")]) - .optional(), - moonshotApiKey: z.string().optional(), +const moonshotProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.moonshot, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + moonshotBaseUrl: z + .union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")]) + .optional(), + moonshotApiKey: z.string().optional(), + }, }) export const kimiCodeAuthMethodSchema = z.enum(["oauth", "api-key"]) export type KimiCodeAuthMethod = z.infer -const kimiCodeSchema = apiModelIdProviderModelSchema.extend({ - kimiCodeAuthMethod: kimiCodeAuthMethodSchema.optional(), - kimiCodeApiKey: z.string().optional(), +const kimiCodeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.kimiCode, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + kimiCodeAuthMethod: kimiCodeAuthMethodSchema.optional(), + kimiCodeApiKey: z.string().optional(), + }, }) -const minimaxSchema = apiModelIdProviderModelSchema.extend({ - minimaxBaseUrl: z - .union([z.literal("https://api.minimax.io/v1"), z.literal("https://api.minimaxi.com/v1")]) - .optional(), - minimaxApiKey: z.string().optional(), +const minimaxProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.minimax, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + minimaxBaseUrl: z + .union([z.literal("https://api.minimax.io/v1"), z.literal("https://api.minimaxi.com/v1")]) + .optional(), + minimaxApiKey: z.string().optional(), + }, }) -const mimoSchema = apiModelIdProviderModelSchema.extend({ - mimoBaseUrl: z - .union([ - z.literal("https://api.xiaomimimo.com/v1"), - z.literal("https://token-plan-cn.xiaomimimo.com/v1"), - z.literal("https://token-plan-sgp.xiaomimimo.com/v1"), - z.literal("https://token-plan-ams.xiaomimimo.com/v1"), - ]) - .optional(), - mimoApiKey: z.string().optional(), +const mimoProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.mimo, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + mimoBaseUrl: z + .union([ + z.literal("https://api.xiaomimimo.com/v1"), + z.literal("https://token-plan-cn.xiaomimimo.com/v1"), + z.literal("https://token-plan-sgp.xiaomimimo.com/v1"), + z.literal("https://token-plan-ams.xiaomimimo.com/v1"), + ]) + .optional(), + mimoApiKey: z.string().optional(), + }, }) -const requestySchema = baseProviderSettingsSchema.extend({ - requestyBaseUrl: z.string().optional(), - requestyApiKey: z.string().optional(), - requestyModelId: z.string().optional(), +const requestyProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.requesty, + getModelId: (settings) => settings.requestyModelId, + schema: { + ...baseProviderSettingsShape, + requestyBaseUrl: z.string().optional(), + requestyApiKey: z.string().optional(), + requestyModelId: z.string().optional(), + }, }) -const unboundSchema = baseProviderSettingsSchema.extend({ - unboundApiKey: z.string().optional(), - unboundModelId: z.string().optional(), +const unboundProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.unbound, + getModelId: (settings) => settings.unboundModelId, + schema: { + ...baseProviderSettingsShape, + unboundApiKey: z.string().optional(), + unboundModelId: z.string().optional(), + }, }) -const fakeAiSchema = baseProviderSettingsSchema.extend({ - fakeAi: z.unknown().optional(), +const fakeAiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.fakeAi, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + fakeAi: z.unknown().optional(), + }, }) -const xaiSchema = apiModelIdProviderModelSchema.extend({ - xaiApiKey: z.string().optional(), +const xaiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.xai, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + xaiApiKey: z.string().optional(), + }, }) -const litellmSchema = baseProviderSettingsSchema.extend({ - litellmBaseUrl: z.string().optional(), - litellmApiKey: z.string().optional(), - litellmModelId: z.string().optional(), - litellmUsePromptCache: z.boolean().optional(), +const litellmProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.litellm, + getModelId: (settings) => settings.litellmModelId, + schema: { + ...baseProviderSettingsShape, + litellmBaseUrl: z.string().optional(), + litellmApiKey: z.string().optional(), + litellmModelId: z.string().optional(), + litellmUsePromptCache: z.boolean().optional(), + }, }) -const sambaNovaSchema = apiModelIdProviderModelSchema.extend({ - sambaNovaApiKey: z.string().optional(), +const sambaNovaProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.sambanova, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + sambaNovaApiKey: z.string().optional(), + }, }) export const zaiApiLineSchema = z.enum(["international_coding", "china_coding", "international_api", "china_api"]) export type ZaiApiLine = z.infer -const zaiSchema = apiModelIdProviderModelSchema.extend({ - zaiApiKey: z.string().optional(), - zaiApiLine: zaiApiLineSchema.optional(), +const zaiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.zai, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + zaiApiKey: z.string().optional(), + zaiApiLine: zaiApiLineSchema.optional(), + }, }) -const fireworksSchema = apiModelIdProviderModelSchema.extend({ - fireworksApiKey: z.string().optional(), +const fireworksProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.fireworks, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + fireworksApiKey: z.string().optional(), + }, }) -const friendliSchema = apiModelIdProviderModelSchema.extend({ - friendliApiKey: z.string().optional(), +const friendliProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.friendli, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + friendliApiKey: z.string().optional(), + }, }) -const qwenCodeSchema = apiModelIdProviderModelSchema.extend({ - qwenCodeOauthPath: z.string().optional(), +const qwenCodeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.qwenCode, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + qwenCodeOauthPath: z.string().optional(), + }, }) -const vercelAiGatewaySchema = baseProviderSettingsSchema.extend({ - vercelAiGatewayApiKey: z.string().optional(), - vercelAiGatewayModelId: z.string().optional(), +const vercelAiGatewayProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.vercelAiGateway, + getModelId: (settings) => settings.vercelAiGatewayModelId, + schema: { + ...baseProviderSettingsShape, + vercelAiGatewayApiKey: z.string().optional(), + vercelAiGatewayModelId: z.string().optional(), + }, }) -const opencodeGoSchema = baseProviderSettingsSchema.extend({ - opencodeGoApiKey: z.string().optional(), - opencodeGoModelId: z.string().optional(), +const opencodeGoProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.opencodeGo, + getModelId: (settings) => settings.opencodeGoModelId, + schema: { + ...baseProviderSettingsShape, + opencodeGoApiKey: z.string().optional(), + opencodeGoModelId: z.string().optional(), + }, }) -const kenariSchema = baseProviderSettingsSchema.extend({ - kenariApiKey: z.string().optional(), - kenariModelId: z.string().optional(), +const kenariProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.kenari, + getModelId: (settings) => settings.kenariModelId, + schema: { + ...baseProviderSettingsShape, + kenariApiKey: z.string().optional(), + kenariModelId: z.string().optional(), + }, }) -const zooGatewaySchema = baseProviderSettingsSchema.extend({ - zooSessionToken: z.string().optional(), - zooGatewayModelId: z.string().optional(), - zooGatewayBaseUrl: z.string().optional(), +const zooGatewayProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.zooGateway, + getModelId: (settings) => settings.zooGatewayModelId, + schema: { + ...baseProviderSettingsShape, + zooSessionToken: z.string().optional(), + zooGatewayModelId: z.string().optional(), + zooGatewayBaseUrl: z.string().optional(), + }, }) -const basetenSchema = apiModelIdProviderModelSchema.extend({ - basetenApiKey: z.string().optional(), +const basetenProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.baseten, + getModelId: (settings) => settings.apiModelId, + schema: { + ...apiModelIdProviderModelShape, + basetenApiKey: z.string().optional(), + }, }) +type ProviderDefinition = { + apiProvider: ProviderName + settingsShape: z.ZodRawShape + schema: z.ZodDiscriminatedUnionOption + getModelId: ProviderModelIdAccessor +} + +const providerDefinitionList = [ + anthropicProviderDefinition, + openRouterProviderDefinition, + bedrockProviderDefinition, + vertexProviderDefinition, + openAiProviderDefinition, + ollamaProviderDefinition, + vsCodeLmProviderDefinition, + lmStudioProviderDefinition, + geminiProviderDefinition, + geminiCliProviderDefinition, + openAiCodexProviderDefinition, + openAiNativeProviderDefinition, + mistralProviderDefinition, + deepSeekProviderDefinition, + poeProviderDefinition, + moonshotProviderDefinition, + kimiCodeProviderDefinition, + minimaxProviderDefinition, + mimoProviderDefinition, + requestyProviderDefinition, + unboundProviderDefinition, + fakeAiProviderDefinition, + xaiProviderDefinition, + basetenProviderDefinition, + litellmProviderDefinition, + sambaNovaProviderDefinition, + zaiProviderDefinition, + fireworksProviderDefinition, + friendliProviderDefinition, + qwenCodeProviderDefinition, + vercelAiGatewayProviderDefinition, + opencodeGoProviderDefinition, + kenariProviderDefinition, + zooGatewayProviderDefinition, +] as const satisfies readonly ProviderDefinition[] + +type ListedProvider = (typeof providerDefinitionList)[number][typeof API_PROVIDER_FIELD] +const allProvidersAreDefined: Exclude extends never ? true : never = true +void allProvidersAreDefined + +const indexProviderDefinitions = ( + definitions: readonly ProviderDefinition[], +): Partial> => { + const indexedDefinitions: Partial> = {} + + for (const definition of definitions) { + if (indexedDefinitions[definition.apiProvider]) { + console.warn(`Duplicate provider definition ignored: ${definition.apiProvider}`) + } + + indexedDefinitions[definition.apiProvider] ??= definition + } + + for (const provider of providerNames) { + if (!indexedDefinitions[provider]) { + console.warn(`Missing provider definition: ${provider}`) + } + } + + return indexedDefinitions +} + +const providerDefinitions = indexProviderDefinitions(providerDefinitionList) + const defaultSchema = z.object({ - apiProvider: z.undefined(), -}) - -export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [ - anthropicSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.anthropic) })), - openRouterSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openrouter) })), - bedrockSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.bedrock) })), - vertexSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.vertex) })), - openAiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openai) })), - ollamaSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.ollama) })), - vsCodeLmSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.vscodeLm) })), - lmStudioSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.lmstudio) })), - geminiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.gemini) })), - geminiCliSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.geminiCli) })), - openAiCodexSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openaiCodex) })), - openAiNativeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.openaiNative) })), - mistralSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.mistral) })), - deepSeekSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.deepseek) })), - poeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.poe) })), - moonshotSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.moonshot) })), - kimiCodeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.kimiCode) })), - minimaxSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.minimax) })), - mimoSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.mimo) })), - requestySchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.requesty) })), - unboundSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.unbound) })), - fakeAiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.fakeAi) })), - xaiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.xai) })), - basetenSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.baseten) })), - litellmSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.litellm) })), - sambaNovaSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.sambanova) })), - zaiSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.zai) })), - fireworksSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.fireworks) })), - friendliSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.friendli) })), - qwenCodeSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.qwenCode) })), - vercelAiGatewaySchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.vercelAiGateway) })), - opencodeGoSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.opencodeGo) })), - kenariSchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.kenari) })), - zooGatewaySchema.merge(z.object({ apiProvider: z.literal(providerIdentifiers.zooGateway) })), + [API_PROVIDER_FIELD]: z.undefined(), +}) + +type ProviderDefinitionSchemas = { + [K in keyof D]: D[K]["schema"] +} + +const getDiscriminatedSchemas = ( + definitions: D, +): ProviderDefinitionSchemas => { + const [firstDefinition, ...remainingDefinitions] = definitions + return [ + firstDefinition.schema, + ...remainingDefinitions.map((definition) => definition.schema), + ] as ProviderDefinitionSchemas +} + +const providerDiscriminatedSchemas = getDiscriminatedSchemas(providerDefinitionList) + +export const providerSettingsSchemaDiscriminated = z.discriminatedUnion(API_PROVIDER_FIELD, [ + ...providerDiscriminatedSchemas, defaultSchema, ]) +type ProviderSettingsShape = UnionToIntersection<(typeof providerDefinitionList)[number][typeof SETTINGS_SHAPE_FIELD]> + +const providerSettingsObjectSchema = providerDefinitionList.reduce( + (schema, definition) => schema.merge(z.object(definition[SETTINGS_SHAPE_FIELD])), + z.object({}), +) + +const providerSettingsShape = providerSettingsObjectSchema.shape as ProviderSettingsShape + export const providerSettingsSchema = z.object({ - apiProvider: providerNamesWithRetiredSchema.optional(), - ...anthropicSchema.shape, - ...openRouterSchema.shape, - ...bedrockSchema.shape, - ...vertexSchema.shape, - ...openAiSchema.shape, - ...ollamaSchema.shape, - ...vsCodeLmSchema.shape, - ...lmStudioSchema.shape, - ...geminiSchema.shape, - ...geminiCliSchema.shape, - ...openAiCodexSchema.shape, - ...openAiNativeSchema.shape, - ...mistralSchema.shape, - ...deepSeekSchema.shape, - ...poeSchema.shape, - ...moonshotSchema.shape, - ...kimiCodeSchema.shape, - ...minimaxSchema.shape, - ...mimoSchema.shape, - ...requestySchema.shape, - ...unboundSchema.shape, - ...fakeAiSchema.shape, - ...xaiSchema.shape, - ...basetenSchema.shape, - ...litellmSchema.shape, - ...sambaNovaSchema.shape, - ...zaiSchema.shape, - ...fireworksSchema.shape, - ...friendliSchema.shape, - ...qwenCodeSchema.shape, - ...vercelAiGatewaySchema.shape, - ...opencodeGoSchema.shape, - ...kenariSchema.shape, - ...zooGatewaySchema.shape, + [API_PROVIDER_FIELD]: providerNamesWithRetiredSchema.optional(), + ...providerSettingsShape, ...codebaseIndexProviderSchema.shape, }) @@ -516,74 +744,12 @@ export type ProviderSettingsWithId = z.infer { - const modelIdKey = modelIdKeys.find((key) => settings[key]) - return modelIdKey ? settings[modelIdKey] : undefined -} - -/** - * TypicalProvider - */ +export function getModelId(settings: ProviderSettings): string | undefined { + if (!isProviderName(settings.apiProvider)) { + return undefined + } -export type TypicalProvider = Exclude - -export const isTypicalProvider = (key: unknown): key is TypicalProvider => - isProviderName(key) && !isInternalProvider(key) && !isCustomProvider(key) && !isFauxProvider(key) - -export const modelIdKeysByProvider: Record = { - [providerIdentifiers.anthropic]: "apiModelId", - [providerIdentifiers.openrouter]: "openRouterModelId", - [providerIdentifiers.bedrock]: "apiModelId", - [providerIdentifiers.vertex]: "apiModelId", - [providerIdentifiers.openaiCodex]: "apiModelId", - [providerIdentifiers.openaiNative]: "openAiModelId", - [providerIdentifiers.ollama]: "ollamaModelId", - [providerIdentifiers.lmstudio]: "lmStudioModelId", - [providerIdentifiers.gemini]: "apiModelId", - [providerIdentifiers.geminiCli]: "apiModelId", - [providerIdentifiers.mistral]: "apiModelId", - [providerIdentifiers.moonshot]: "apiModelId", - [providerIdentifiers.kimiCode]: "apiModelId", - [providerIdentifiers.minimax]: "apiModelId", - [providerIdentifiers.mimo]: "apiModelId", - [providerIdentifiers.deepseek]: "apiModelId", - [providerIdentifiers.poe]: "apiModelId", - [providerIdentifiers.qwenCode]: "apiModelId", - [providerIdentifiers.requesty]: "requestyModelId", - [providerIdentifiers.unbound]: "unboundModelId", - [providerIdentifiers.xai]: "apiModelId", - [providerIdentifiers.baseten]: "apiModelId", - [providerIdentifiers.litellm]: "litellmModelId", - [providerIdentifiers.sambanova]: "apiModelId", - [providerIdentifiers.zai]: "apiModelId", - [providerIdentifiers.fireworks]: "apiModelId", - [providerIdentifiers.friendli]: "apiModelId", - [providerIdentifiers.vercelAiGateway]: "vercelAiGatewayModelId", - [providerIdentifiers.opencodeGo]: "opencodeGoModelId", - [providerIdentifiers.kenari]: "kenariModelId", - [providerIdentifiers.zooGateway]: "zooGatewayModelId", + return providerDefinitions[settings.apiProvider]?.getModelId(settings) } /** diff --git a/packages/types/src/type-fu.ts b/packages/types/src/type-fu.ts index 0014e9b187..69f558f21a 100644 --- a/packages/types/src/type-fu.ts +++ b/packages/types/src/type-fu.ts @@ -6,6 +6,10 @@ export type Keys = keyof T export type Values = T[keyof T] +export type UnionToIntersection = (U extends unknown ? (value: U) => void : never) extends (value: infer I) => void + ? I + : never + export type Equals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false export type AssertEqual = T diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index b750d8833b..e946906e10 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -3,14 +3,9 @@ import i18next from "i18next" import { type ProviderSettings, type OrganizationAllowList, - type ProviderName, type RouterModels, - modelIdKeysByProvider, - isProviderName, - isRetiredProvider, + getModelId, isDynamicProvider, - isFauxProvider, - isCustomProvider, providerIdentifiers, } from "@roo-code/types" @@ -189,8 +184,7 @@ function validateProviderAgainstOrganizationSettings( } if (!providerConfig.allowAll) { - const activeProvider = isRetiredProvider(provider) ? undefined : provider - const modelId = activeProvider ? getModelIdForProvider(apiConfiguration, activeProvider) : undefined + const modelId = getModelId(apiConfiguration) const allowedModels = providerConfig.models || [] if (modelId && !allowedModels.includes(modelId)) { @@ -206,18 +200,6 @@ function validateProviderAgainstOrganizationSettings( } } -function getModelIdForProvider(apiConfiguration: ProviderSettings, provider: ProviderName): string | undefined { - if (provider === providerIdentifiers.vscodeLm) { - return apiConfiguration.vsCodeLmModelSelector?.id - } - - if (isCustomProvider(provider) || isFauxProvider(provider)) { - return apiConfiguration.apiModelId - } - - return apiConfiguration[modelIdKeysByProvider[provider]] -} - /** * Validates an Amazon Bedrock ARN and optionally checks if the region in * the ARN matches the provided region. @@ -261,7 +243,7 @@ function validateDynamicProviderModelId( return undefined } - const modelId = getModelIdForProvider(apiConfiguration, provider) + const modelId = getModelId(apiConfiguration) if (!modelId) { return i18next.t("settings:validation.modelId") @@ -285,9 +267,7 @@ export function getModelValidationError( routerModels?: RouterModels, organizationAllowList?: OrganizationAllowList, ): string | undefined { - const modelId = isProviderName(apiConfiguration.apiProvider) - ? getModelIdForProvider(apiConfiguration, apiConfiguration.apiProvider) - : apiConfiguration.apiModelId + const modelId = getModelId(apiConfiguration) ?? apiConfiguration.apiModelId const configWithModelId = { ...apiConfiguration, From 3c2f92d6981878214b4e91ce7f6be7e721ad8b96 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 14:34:18 +0300 Subject: [PATCH 02/16] fix(types): preserve provider settings compatibility --- packages/types/src/provider-settings.ts | 83 ++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 1e4b8cc1e1..3f98a9483a 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -744,12 +744,89 @@ export type ProviderSettingsWithId = z.infer + +/** + * @deprecated Use the specific provider type guards instead. + */ +export const isTypicalProvider = (key: unknown): key is TypicalProvider => + isProviderName(key) && !isInternalProvider(key) && !isCustomProvider(key) && !isFauxProvider(key) + +/** + * @deprecated Use `getModelId()` instead. This map is retained for API compatibility. + */ +export const modelIdKeysByProvider: Record = { + [providerIdentifiers.anthropic]: "apiModelId", + [providerIdentifiers.openrouter]: "openRouterModelId", + [providerIdentifiers.bedrock]: "apiModelId", + [providerIdentifiers.vertex]: "apiModelId", + [providerIdentifiers.openaiCodex]: "apiModelId", + [providerIdentifiers.openaiNative]: "apiModelId", + [providerIdentifiers.ollama]: "ollamaModelId", + [providerIdentifiers.lmstudio]: "lmStudioModelId", + [providerIdentifiers.gemini]: "apiModelId", + [providerIdentifiers.geminiCli]: "apiModelId", + [providerIdentifiers.mistral]: "apiModelId", + [providerIdentifiers.moonshot]: "apiModelId", + [providerIdentifiers.kimiCode]: "apiModelId", + [providerIdentifiers.minimax]: "apiModelId", + [providerIdentifiers.mimo]: "apiModelId", + [providerIdentifiers.deepseek]: "apiModelId", + [providerIdentifiers.poe]: "apiModelId", + [providerIdentifiers.qwenCode]: "apiModelId", + [providerIdentifiers.requesty]: "requestyModelId", + [providerIdentifiers.unbound]: "unboundModelId", + [providerIdentifiers.xai]: "apiModelId", + [providerIdentifiers.baseten]: "apiModelId", + [providerIdentifiers.litellm]: "litellmModelId", + [providerIdentifiers.sambanova]: "apiModelId", + [providerIdentifiers.zai]: "apiModelId", + [providerIdentifiers.fireworks]: "apiModelId", + [providerIdentifiers.friendli]: "apiModelId", + [providerIdentifiers.vercelAiGateway]: "vercelAiGatewayModelId", + [providerIdentifiers.opencodeGo]: "opencodeGoModelId", + [providerIdentifiers.kenari]: "kenariModelId", + [providerIdentifiers.zooGateway]: "zooGatewayModelId", +} + export function getModelId(settings: ProviderSettings): string | undefined { - if (!isProviderName(settings.apiProvider)) { - return undefined + if (isProviderName(settings.apiProvider)) { + return providerDefinitions[settings.apiProvider]?.getModelId(settings) + } + + if (typeof settings.apiProvider === "string" && isRetiredProvider(settings.apiProvider)) { + const modelIdKey = modelIdKeys.find((key) => settings[key]) + return modelIdKey ? settings[modelIdKey] : undefined } - return providerDefinitions[settings.apiProvider]?.getModelId(settings) + return undefined } /** From 38e5d3a9a79461d2cb06e9a31a006c58eca23904 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 14:48:14 +0300 Subject: [PATCH 03/16] refactor(types): derive model keys from provider definitions --- packages/types/src/provider-settings.ts | 203 +++++++++++------------- 1 file changed, 97 insertions(+), 106 deletions(-) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 3f98a9483a..fa95c86652 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -163,6 +163,17 @@ export type ProviderNameWithRetired = z.infer) => string | u type TypedProviderModelIdAccessor = (settings: z.infer>) => string | undefined -const createProviderDefinition =

({ +const createProviderDefinition =

>({ apiProvider, schema, - getModelId, + ...modelIdDefinition }: { apiProvider: P schema: S - getModelId: TypedProviderModelIdAccessor -}) => ({ - apiProvider, - settingsShape: schema, - schema: z.object({ - ...schema, - [API_PROVIDER_FIELD]: z.literal(apiProvider), - }), - getModelId: ((settings) => getModelId(settings as z.infer>)) satisfies ProviderModelIdAccessor, -}) +} & ({ modelIdKey: K; getModelId?: never } | { modelIdKey?: never; getModelId: TypedProviderModelIdAccessor })) => { + const getModelId: TypedProviderModelIdAccessor = + modelIdDefinition.getModelId ?? ((settings) => settings[modelIdDefinition.modelIdKey] as string | undefined) + + return { + apiProvider, + settingsShape: schema, + modelIdKey: modelIdDefinition.modelIdKey, + schema: z.object({ + ...schema, + [API_PROVIDER_FIELD]: z.literal(apiProvider), + }), + getModelId: ((settings) => getModelId(settings as z.infer>)) satisfies ProviderModelIdAccessor, + } +} // Several of the providers share common model config properties. const apiModelIdProviderModelShape = { ...baseProviderSettingsShape, - apiModelId: z.string().optional(), + [API_MODEL_ID_FIELD]: z.string().optional(), } const anthropicProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.anthropic, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, apiKey: z.string().optional(), @@ -236,11 +252,11 @@ const anthropicProviderDefinition = createProviderDefinition({ const openRouterProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openrouter, - getModelId: (settings) => settings.openRouterModelId, + modelIdKey: OPEN_ROUTER_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, openRouterApiKey: z.string().optional(), - openRouterModelId: z.string().optional(), + [OPEN_ROUTER_MODEL_ID_FIELD]: z.string().optional(), openRouterBaseUrl: z.string().optional(), openRouterSpecificProvider: z.string().optional(), }, @@ -248,7 +264,7 @@ const openRouterProviderDefinition = createProviderDefinition({ const bedrockProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.bedrock, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, awsAccessKey: z.string().optional(), @@ -273,7 +289,7 @@ const bedrockProviderDefinition = createProviderDefinition({ const vertexProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.vertex, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, vertexKeyFile: z.string().optional(), @@ -286,13 +302,13 @@ const vertexProviderDefinition = createProviderDefinition({ const openAiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openai, - getModelId: (settings) => settings.openAiModelId, + modelIdKey: OPEN_AI_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, openAiBaseUrl: z.string().optional(), openAiApiKey: z.string().optional(), openAiR1FormatEnabled: z.boolean().optional(), - openAiModelId: z.string().optional(), + [OPEN_AI_MODEL_ID_FIELD]: z.string().optional(), openAiCustomModelInfo: modelInfoSchema.nullish(), openAiUseAzure: z.boolean().optional(), azureApiVersion: z.string().optional(), @@ -304,10 +320,10 @@ const openAiProviderDefinition = createProviderDefinition({ const ollamaProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.ollama, - getModelId: (settings) => settings.ollamaModelId, + modelIdKey: OLLAMA_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, - ollamaModelId: z.string().optional(), + [OLLAMA_MODEL_ID_FIELD]: z.string().optional(), ollamaBaseUrl: z.string().optional(), ollamaApiKey: z.string().optional(), ollamaNumCtx: z.number().int().min(128).optional(), @@ -332,10 +348,10 @@ const vsCodeLmProviderDefinition = createProviderDefinition({ const lmStudioProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.lmstudio, - getModelId: (settings) => settings.lmStudioModelId, + modelIdKey: LM_STUDIO_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, - lmStudioModelId: z.string().optional(), + [LM_STUDIO_MODEL_ID_FIELD]: z.string().optional(), lmStudioBaseUrl: z.string().optional(), lmStudioDraftModelId: z.string().optional(), lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), @@ -344,7 +360,7 @@ const lmStudioProviderDefinition = createProviderDefinition({ const geminiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.gemini, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, geminiApiKey: z.string().optional(), @@ -354,7 +370,7 @@ const geminiProviderDefinition = createProviderDefinition({ const geminiCliProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.geminiCli, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, geminiCliOAuthPath: z.string().optional(), @@ -364,7 +380,7 @@ const geminiCliProviderDefinition = createProviderDefinition({ const openAiCodexProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openaiCodex, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, // Codex "Fast" mode maps to the Responses API priority service tier. @@ -374,7 +390,7 @@ const openAiCodexProviderDefinition = createProviderDefinition({ const openAiNativeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openaiNative, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, openAiNativeApiKey: z.string().optional(), @@ -387,7 +403,7 @@ const openAiNativeProviderDefinition = createProviderDefinition({ const mistralProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.mistral, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, mistralApiKey: z.string().optional(), @@ -397,7 +413,7 @@ const mistralProviderDefinition = createProviderDefinition({ const deepSeekProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.deepseek, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, deepSeekBaseUrl: z.string().optional(), @@ -407,7 +423,7 @@ const deepSeekProviderDefinition = createProviderDefinition({ const poeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.poe, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, poeApiKey: z.string().optional(), @@ -417,7 +433,7 @@ const poeProviderDefinition = createProviderDefinition({ const moonshotProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.moonshot, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, moonshotBaseUrl: z @@ -432,7 +448,7 @@ export type KimiCodeAuthMethod = z.infer const kimiCodeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.kimiCode, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, kimiCodeAuthMethod: kimiCodeAuthMethodSchema.optional(), @@ -442,7 +458,7 @@ const kimiCodeProviderDefinition = createProviderDefinition({ const minimaxProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.minimax, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, minimaxBaseUrl: z @@ -454,7 +470,7 @@ const minimaxProviderDefinition = createProviderDefinition({ const mimoProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.mimo, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, mimoBaseUrl: z @@ -471,28 +487,28 @@ const mimoProviderDefinition = createProviderDefinition({ const requestyProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.requesty, - getModelId: (settings) => settings.requestyModelId, + modelIdKey: REQUESTY_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, requestyBaseUrl: z.string().optional(), requestyApiKey: z.string().optional(), - requestyModelId: z.string().optional(), + [REQUESTY_MODEL_ID_FIELD]: z.string().optional(), }, }) const unboundProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.unbound, - getModelId: (settings) => settings.unboundModelId, + modelIdKey: UNBOUND_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, unboundApiKey: z.string().optional(), - unboundModelId: z.string().optional(), + [UNBOUND_MODEL_ID_FIELD]: z.string().optional(), }, }) const fakeAiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.fakeAi, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, fakeAi: z.unknown().optional(), @@ -501,7 +517,7 @@ const fakeAiProviderDefinition = createProviderDefinition({ const xaiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.xai, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, xaiApiKey: z.string().optional(), @@ -510,19 +526,19 @@ const xaiProviderDefinition = createProviderDefinition({ const litellmProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.litellm, - getModelId: (settings) => settings.litellmModelId, + modelIdKey: LITELLM_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, litellmBaseUrl: z.string().optional(), litellmApiKey: z.string().optional(), - litellmModelId: z.string().optional(), + [LITELLM_MODEL_ID_FIELD]: z.string().optional(), litellmUsePromptCache: z.boolean().optional(), }, }) const sambaNovaProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.sambanova, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, sambaNovaApiKey: z.string().optional(), @@ -535,7 +551,7 @@ export type ZaiApiLine = z.infer const zaiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.zai, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, zaiApiKey: z.string().optional(), @@ -545,7 +561,7 @@ const zaiProviderDefinition = createProviderDefinition({ const fireworksProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.fireworks, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, fireworksApiKey: z.string().optional(), @@ -554,7 +570,7 @@ const fireworksProviderDefinition = createProviderDefinition({ const friendliProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.friendli, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, friendliApiKey: z.string().optional(), @@ -563,7 +579,7 @@ const friendliProviderDefinition = createProviderDefinition({ const qwenCodeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.qwenCode, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, qwenCodeOauthPath: z.string().optional(), @@ -572,48 +588,50 @@ const qwenCodeProviderDefinition = createProviderDefinition({ const vercelAiGatewayProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.vercelAiGateway, - getModelId: (settings) => settings.vercelAiGatewayModelId, + modelIdKey: VERCEL_AI_GATEWAY_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, vercelAiGatewayApiKey: z.string().optional(), - vercelAiGatewayModelId: z.string().optional(), + [VERCEL_AI_GATEWAY_MODEL_ID_FIELD]: z.string().optional(), }, }) const opencodeGoProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.opencodeGo, - getModelId: (settings) => settings.opencodeGoModelId, + modelIdKey: OPENCODE_GO_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, opencodeGoApiKey: z.string().optional(), - opencodeGoModelId: z.string().optional(), + [OPENCODE_GO_MODEL_ID_FIELD]: z.string().optional(), }, }) +const KENARI_MODEL_ID_FIELD = "kenariModelId" + const kenariProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.kenari, - getModelId: (settings) => settings.kenariModelId, + modelIdKey: KENARI_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, kenariApiKey: z.string().optional(), - kenariModelId: z.string().optional(), + [KENARI_MODEL_ID_FIELD]: z.string().optional(), }, }) const zooGatewayProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.zooGateway, - getModelId: (settings) => settings.zooGatewayModelId, + modelIdKey: ZOO_GATEWAY_MODEL_ID_FIELD, schema: { ...baseProviderSettingsShape, zooSessionToken: z.string().optional(), - zooGatewayModelId: z.string().optional(), + [ZOO_GATEWAY_MODEL_ID_FIELD]: z.string().optional(), zooGatewayBaseUrl: z.string().optional(), }, }) const basetenProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.baseten, - getModelId: (settings) => settings.apiModelId, + modelIdKey: API_MODEL_ID_FIELD, schema: { ...apiModelIdProviderModelShape, basetenApiKey: z.string().optional(), @@ -623,6 +641,7 @@ const basetenProviderDefinition = createProviderDefinition({ type ProviderDefinition = { apiProvider: ProviderName settingsShape: z.ZodRawShape + modelIdKey?: string schema: z.ZodDiscriminatedUnionOption getModelId: ProviderModelIdAccessor } @@ -747,26 +766,18 @@ export const PROVIDER_SETTINGS_KEYS = providerSettingsSchema.keyof().options /** * @deprecated Use `getModelId()` to resolve the model ID for the active provider. */ -export const modelIdKeys = [ - "apiModelId", - "openRouterModelId", - "openAiModelId", - "ollamaModelId", - "lmStudioModelId", - "lmStudioDraftModelId", - "requestyModelId", - "unboundModelId", - "litellmModelId", - "vercelAiGatewayModelId", - "opencodeGoModelId", - "kenariModelId", - "zooGatewayModelId", -] as const satisfies readonly (keyof ProviderSettings)[] +export type ModelIdKey = Extract /** * @deprecated Use `getModelId()` to resolve the model ID for the active provider. */ -export type ModelIdKey = (typeof modelIdKeys)[number] +export const modelIdKeys = [ + ...new Set( + providerDefinitionList.flatMap((definition) => + definition.modelIdKey ? [definition.modelIdKey as ModelIdKey] : [], + ), + ), +] as const /** * @deprecated Provider categories should use the specific provider type guards. @@ -782,39 +793,19 @@ export const isTypicalProvider = (key: unknown): key is TypicalProvider => /** * @deprecated Use `getModelId()` instead. This map is retained for API compatibility. */ -export const modelIdKeysByProvider: Record = { - [providerIdentifiers.anthropic]: "apiModelId", - [providerIdentifiers.openrouter]: "openRouterModelId", - [providerIdentifiers.bedrock]: "apiModelId", - [providerIdentifiers.vertex]: "apiModelId", - [providerIdentifiers.openaiCodex]: "apiModelId", - [providerIdentifiers.openaiNative]: "apiModelId", - [providerIdentifiers.ollama]: "ollamaModelId", - [providerIdentifiers.lmstudio]: "lmStudioModelId", - [providerIdentifiers.gemini]: "apiModelId", - [providerIdentifiers.geminiCli]: "apiModelId", - [providerIdentifiers.mistral]: "apiModelId", - [providerIdentifiers.moonshot]: "apiModelId", - [providerIdentifiers.kimiCode]: "apiModelId", - [providerIdentifiers.minimax]: "apiModelId", - [providerIdentifiers.mimo]: "apiModelId", - [providerIdentifiers.deepseek]: "apiModelId", - [providerIdentifiers.poe]: "apiModelId", - [providerIdentifiers.qwenCode]: "apiModelId", - [providerIdentifiers.requesty]: "requestyModelId", - [providerIdentifiers.unbound]: "unboundModelId", - [providerIdentifiers.xai]: "apiModelId", - [providerIdentifiers.baseten]: "apiModelId", - [providerIdentifiers.litellm]: "litellmModelId", - [providerIdentifiers.sambanova]: "apiModelId", - [providerIdentifiers.zai]: "apiModelId", - [providerIdentifiers.fireworks]: "apiModelId", - [providerIdentifiers.friendli]: "apiModelId", - [providerIdentifiers.vercelAiGateway]: "vercelAiGatewayModelId", - [providerIdentifiers.opencodeGo]: "opencodeGoModelId", - [providerIdentifiers.kenari]: "kenariModelId", - [providerIdentifiers.zooGateway]: "zooGatewayModelId", -} +export const modelIdKeysByProvider = Object.fromEntries( + providerDefinitionList.flatMap((definition) => { + if (!isTypicalProvider(definition.apiProvider)) { + return [] + } + + if (!definition.modelIdKey) { + throw new Error(`Missing model ID key for provider definition: ${definition.apiProvider}`) + } + + return [[definition.apiProvider, definition.modelIdKey] as const] + }), +) as Record export function getModelId(settings: ProviderSettings): string | undefined { if (isProviderName(settings.apiProvider)) { From c28a126884bc0eaf569bdcd09cf30415d48a765d Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 14:55:18 +0300 Subject: [PATCH 04/16] docs(types): clarify model key compatibility --- packages/types/src/provider-settings.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index fa95c86652..668ffd6b66 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -209,6 +209,7 @@ type ProviderModelIdAccessor = (settings: Record) => string | u type TypedProviderModelIdAccessor = (settings: z.infer>) => string | undefined +// `modelIdKey` supports deprecated exports. Remove it in favor of an accessor-only contract when those exports are removed. const createProviderDefinition =

>({ apiProvider, schema, From 3345f25e2850c917bf2bc53510fe52f151a60de2 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 15:07:05 +0300 Subject: [PATCH 05/16] fix(types): preserve legacy model id keys --- .../src/__tests__/provider-model-id.test.ts | 22 ++++++++++++++++++- packages/types/src/provider-settings.ts | 3 +++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/types/src/__tests__/provider-model-id.test.ts b/packages/types/src/__tests__/provider-model-id.test.ts index c9ac8e869c..27903e9500 100644 --- a/packages/types/src/__tests__/provider-model-id.test.ts +++ b/packages/types/src/__tests__/provider-model-id.test.ts @@ -1,4 +1,24 @@ -import { getModelId, providerIdentifiers, type ProviderSettings } from "../index.js" +import { getModelId, modelIdKeys, providerIdentifiers, type ProviderSettings } from "../index.js" + +describe("modelIdKeys", () => { + it("preserves every model ID setting for compatibility", () => { + expect(modelIdKeys).toEqual([ + "apiModelId", + "openRouterModelId", + "openAiModelId", + "ollamaModelId", + "lmStudioModelId", + "requestyModelId", + "unboundModelId", + "litellmModelId", + "vercelAiGatewayModelId", + "opencodeGoModelId", + "kenariModelId", + "zooGatewayModelId", + "lmStudioDraftModelId", + ]) + }) +}) describe("getModelId", () => { it("uses the provider-specific model ID field", () => { diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 668ffd6b66..d4186a8fb4 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -769,6 +769,8 @@ export const PROVIDER_SETTINGS_KEYS = providerSettingsSchema.keyof().options */ export type ModelIdKey = Extract +const legacyModelIdKeys = ["lmStudioDraftModelId"] as const satisfies readonly ModelIdKey[] + /** * @deprecated Use `getModelId()` to resolve the model ID for the active provider. */ @@ -778,6 +780,7 @@ export const modelIdKeys = [ definition.modelIdKey ? [definition.modelIdKey as ModelIdKey] : [], ), ), + ...legacyModelIdKeys, ] as const /** From 492d5fbf819460aeaf3f84aeeb39972a5eb3e59c Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 15:32:19 +0300 Subject: [PATCH 06/16] fix(types): preserve model id key compatibility --- .../src/__tests__/provider-model-id.test.ts | 31 ++++++++++++++++++- packages/types/src/provider-settings.ts | 29 ++++++++++------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/packages/types/src/__tests__/provider-model-id.test.ts b/packages/types/src/__tests__/provider-model-id.test.ts index 27903e9500..5a6e40546e 100644 --- a/packages/types/src/__tests__/provider-model-id.test.ts +++ b/packages/types/src/__tests__/provider-model-id.test.ts @@ -1,5 +1,24 @@ import { getModelId, modelIdKeys, providerIdentifiers, type ProviderSettings } from "../index.js" +type ExpectedModelIdKeys = readonly [ + "apiModelId", + "openRouterModelId", + "openAiModelId", + "ollamaModelId", + "lmStudioModelId", + "lmStudioDraftModelId", + "requestyModelId", + "unboundModelId", + "litellmModelId", + "vercelAiGatewayModelId", + "opencodeGoModelId", + "kenariModelId", + "zooGatewayModelId", +] + +const exactModelIdKeys: ExpectedModelIdKeys = modelIdKeys +void exactModelIdKeys + describe("modelIdKeys", () => { it("preserves every model ID setting for compatibility", () => { expect(modelIdKeys).toEqual([ @@ -8,6 +27,7 @@ describe("modelIdKeys", () => { "openAiModelId", "ollamaModelId", "lmStudioModelId", + "lmStudioDraftModelId", "requestyModelId", "unboundModelId", "litellmModelId", @@ -15,7 +35,6 @@ describe("modelIdKeys", () => { "opencodeGoModelId", "kenariModelId", "zooGatewayModelId", - "lmStudioDraftModelId", ]) }) }) @@ -70,6 +89,16 @@ describe("getModelId", () => { expect(getModelId({})).toBeUndefined() }) + it("preserves legacy model ID precedence for retired providers", () => { + const settings: ProviderSettings = { + apiProvider: "groq", + lmStudioDraftModelId: "draft-model", + requestyModelId: "requesty-model", + } + + expect(getModelId(settings)).toBe("draft-model") + }) + it("resolves a model ID for every provider definition without throwing", () => { for (const apiProvider of Object.values(providerIdentifiers)) { expect(() => getModelId({ apiProvider })).not.toThrow() diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index d4186a8fb4..d041996d80 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -134,7 +134,7 @@ export const providerNamesSchema = z.enum(providerNames) export type ProviderName = z.infer -export const isProviderName = (key: unknown): key is ProviderName => +export const isActiveProviderName = (key: unknown): key is ProviderName => typeof key === "string" && providerNames.includes(key as ProviderName) /** @@ -769,19 +769,24 @@ export const PROVIDER_SETTINGS_KEYS = providerSettingsSchema.keyof().options */ export type ModelIdKey = Extract -const legacyModelIdKeys = ["lmStudioDraftModelId"] as const satisfies readonly ModelIdKey[] - /** * @deprecated Use `getModelId()` to resolve the model ID for the active provider. */ export const modelIdKeys = [ - ...new Set( - providerDefinitionList.flatMap((definition) => - definition.modelIdKey ? [definition.modelIdKey as ModelIdKey] : [], - ), - ), - ...legacyModelIdKeys, -] as const + "apiModelId", + "openRouterModelId", + "openAiModelId", + "ollamaModelId", + "lmStudioModelId", + "lmStudioDraftModelId", + "requestyModelId", + "unboundModelId", + "litellmModelId", + "vercelAiGatewayModelId", + "opencodeGoModelId", + "kenariModelId", + "zooGatewayModelId", +] as const satisfies readonly ModelIdKey[] /** * @deprecated Provider categories should use the specific provider type guards. @@ -792,7 +797,7 @@ export type TypicalProvider = Exclude - isProviderName(key) && !isInternalProvider(key) && !isCustomProvider(key) && !isFauxProvider(key) + isActiveProviderName(key) && !isInternalProvider(key) && !isCustomProvider(key) && !isFauxProvider(key) /** * @deprecated Use `getModelId()` instead. This map is retained for API compatibility. @@ -812,7 +817,7 @@ export const modelIdKeysByProvider = Object.fromEntries( ) as Record export function getModelId(settings: ProviderSettings): string | undefined { - if (isProviderName(settings.apiProvider)) { + if (isActiveProviderName(settings.apiProvider)) { return providerDefinitions[settings.apiProvider]?.getModelId(settings) } From 4e63d0a0f21d37fd767db10eeab439d6eea7846a Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 15:47:11 +0300 Subject: [PATCH 07/16] fix(core): adopt active provider type guard --- .../types/src/__tests__/provider-identifiers.test.ts | 10 +++++----- src/core/config/ContextProxy.ts | 6 +++--- src/core/config/ProviderSettingsManager.ts | 4 ++-- src/core/config/importExport.ts | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/types/src/__tests__/provider-identifiers.test.ts b/packages/types/src/__tests__/provider-identifiers.test.ts index 870ce77d78..6959b438b4 100644 --- a/packages/types/src/__tests__/provider-identifiers.test.ts +++ b/packages/types/src/__tests__/provider-identifiers.test.ts @@ -8,7 +8,7 @@ import { isFauxProvider, isInternalProvider, isLocalProvider, - isProviderName, + isActiveProviderName, isRetiredProvider, localProviders, MODELS_BY_PROVIDER, @@ -195,17 +195,17 @@ describe("provider identifiers", () => { it("preserves provider-settings type guards", () => { for (const identifier of expectedProviderIdentifiers) { - expect(isProviderName(identifier)).toBe(true) + expect(isActiveProviderName(identifier)).toBe(true) expect(isRetiredProvider(identifier)).toBe(false) } for (const identifier of expectedRetiredProviderIdentifiers) { - expect(isProviderName(identifier)).toBe(false) + expect(isActiveProviderName(identifier)).toBe(false) expect(isRetiredProvider(identifier)).toBe(true) } - expect(isProviderName("unknown-provider")).toBe(false) - expect(isProviderName(undefined)).toBe(false) + expect(isActiveProviderName("unknown-provider")).toBe(false) + expect(isActiveProviderName(undefined)).toBe(false) expect(isRetiredProvider("unknown-provider")).toBe(false) }) }) diff --git a/src/core/config/ContextProxy.ts b/src/core/config/ContextProxy.ts index 97d4104afc..a9f25491b3 100644 --- a/src/core/config/ContextProxy.ts +++ b/src/core/config/ContextProxy.ts @@ -15,7 +15,7 @@ import { providerSettingsSchema, globalSettingsSchema, isSecretStateKey, - isProviderName, + isActiveProviderName, isRetiredProvider, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" @@ -262,7 +262,7 @@ export class ContextProxy { try { const apiProvider = this.stateCache.apiProvider const isKnownProvider = - typeof apiProvider === "string" && (isProviderName(apiProvider) || isRetiredProvider(apiProvider)) + typeof apiProvider === "string" && (isActiveProviderName(apiProvider) || isRetiredProvider(apiProvider)) if (apiProvider !== undefined && !isKnownProvider) { logger.info(`[ContextProxy] Found invalid provider "${apiProvider}" in storage - clearing it`) @@ -494,7 +494,7 @@ export class ContextProxy { const isKnownProvider = typeof sanitizedValues.apiProvider === "string" && - (isProviderName(sanitizedValues.apiProvider) || isRetiredProvider(sanitizedValues.apiProvider)) + (isActiveProviderName(sanitizedValues.apiProvider) || isRetiredProvider(sanitizedValues.apiProvider)) if (sanitizedValues.apiProvider !== undefined && !isKnownProvider) { logger.info( diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index 51f79cff35..efb240037b 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -11,7 +11,7 @@ import { DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, getModelId, type ProviderName, - isProviderName, + isActiveProviderName, isRetiredProvider, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" @@ -688,7 +688,7 @@ export class ProviderSettingsManager { // Check if apiProvider is set and if it's still recognized (active or retired) if ( apiProvider !== undefined && - (typeof apiProvider !== "string" || (!isProviderName(apiProvider) && !isRetiredProvider(apiProvider))) + (typeof apiProvider !== "string" || (!isActiveProviderName(apiProvider) && !isRetiredProvider(apiProvider))) ) { console.log( `[ProviderSettingsManager] Sanitizing unknown provider "${config.apiProvider}" - resetting to undefined`, diff --git a/src/core/config/importExport.ts b/src/core/config/importExport.ts index b5fd5fdf98..c819a9aaf1 100644 --- a/src/core/config/importExport.ts +++ b/src/core/config/importExport.ts @@ -9,7 +9,7 @@ import { z, ZodError } from "zod" import { globalSettingsSchema, providerSettingsWithIdSchema, - isProviderName, + isActiveProviderName, type GlobalSettings, type ProviderSettingsWithId, } from "@roo-code/types" @@ -58,7 +58,7 @@ function sanitizeProviderConfig(configName: string, apiConfig: unknown): { confi } // Check if apiProvider is set and if it's still valid - if (config.apiProvider !== undefined && !isProviderName(config.apiProvider)) { + if (config.apiProvider !== undefined && !isActiveProviderName(config.apiProvider)) { const invalidProvider = config.apiProvider // Return a new config object without the invalid apiProvider const { apiProvider, ...restConfig } = config From 871b5dc212eff3dc75f8ae3e8b55f0b36b7dd662 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 15:58:37 +0300 Subject: [PATCH 08/16] fix(types): preserve provider guard compatibility --- .../__tests__/provider-identifiers.test.ts | 6 ++++ packages/types/src/provider-settings.ts | 8 +++++ .../src/utils/__tests__/validate.spec.ts | 30 +++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/packages/types/src/__tests__/provider-identifiers.test.ts b/packages/types/src/__tests__/provider-identifiers.test.ts index 6959b438b4..0768a1a487 100644 --- a/packages/types/src/__tests__/provider-identifiers.test.ts +++ b/packages/types/src/__tests__/provider-identifiers.test.ts @@ -9,6 +9,7 @@ import { isInternalProvider, isLocalProvider, isActiveProviderName, + isProviderName, isRetiredProvider, localProviders, MODELS_BY_PROVIDER, @@ -208,4 +209,9 @@ describe("provider identifiers", () => { expect(isActiveProviderName(undefined)).toBe(false) expect(isRetiredProvider("unknown-provider")).toBe(false) }) + + it("preserves the deprecated isProviderName compatibility export", () => { + expect(isProviderName(providerIdentifiers.anthropic)).toBe(true) + expect(isProviderName("unknown-provider")).toBe(false) + }) }) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index d041996d80..2a3486bd08 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -137,6 +137,11 @@ export type ProviderName = z.infer export const isActiveProviderName = (key: unknown): key is ProviderName => typeof key === "string" && providerNames.includes(key as ProviderName) +/** + * @deprecated Use `isActiveProviderName()` instead. + */ +export const isProviderName = isActiveProviderName + /** * RetiredProviderName */ @@ -693,6 +698,7 @@ const indexProviderDefinitions = ( ): Partial> => { const indexedDefinitions: Partial> = {} + // Keep registry construction non-throwing so a malformed definition cannot prevent the extension from starting in production. for (const definition of definitions) { if (indexedDefinitions[definition.apiProvider]) { console.warn(`Duplicate provider definition ignored: ${definition.apiProvider}`) @@ -724,6 +730,7 @@ const getDiscriminatedSchemas = => { const [firstDefinition, ...remainingDefinitions] = definitions + // Array mapping widens the tuple, so restore the per-definition schema tuple type expected by Zod. return [ firstDefinition.schema, ...remainingDefinitions.map((definition) => definition.schema), @@ -744,6 +751,7 @@ const providerSettingsObjectSchema = providerDefinitionList.reduce { expect(result).toBeUndefined() }) + it.each([ + { + name: "OpenAI Native", + config: { apiProvider: "openai-native", apiModelId: "blocked-model" } satisfies ProviderSettings, + }, + { + name: "OpenAI Compatible", + config: { apiProvider: "openai", openAiModelId: "blocked-model" } satisfies ProviderSettings, + }, + { + name: "VS Code LM", + config: { + apiProvider: "vscode-lm", + vsCodeLmModelSelector: { id: "blocked-model" }, + } satisfies ProviderSettings, + }, + ])("uses the provider-specific model field for $name organization validation", ({ config }) => { + const organizationAllowList: OrganizationAllowList = { + allowAll: false, + providers: { + [config.apiProvider!]: { allowAll: false, models: ["allowed-model"] }, + }, + } + + const result = getModelValidationError(config, undefined, organizationAllowList) + + expect(result).toContain("settings:validation.modelNotAllowed") + expect(result).toContain("model=blocked-model") + }) + it("handles empty model IDs gracefully", () => { const config: ProviderSettings = { apiProvider: "openrouter", From 10df0054463b75351031f399e4e3b35a508e8dfd Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 16:02:10 +0300 Subject: [PATCH 09/16] refactor(types): retain concise provider guard name --- .../src/__tests__/provider-identifiers.test.ts | 14 ++++---------- packages/types/src/provider-settings.ts | 11 +++-------- src/core/config/ContextProxy.ts | 6 +++--- src/core/config/ProviderSettingsManager.ts | 4 ++-- src/core/config/importExport.ts | 4 ++-- 5 files changed, 14 insertions(+), 25 deletions(-) diff --git a/packages/types/src/__tests__/provider-identifiers.test.ts b/packages/types/src/__tests__/provider-identifiers.test.ts index 0768a1a487..870ce77d78 100644 --- a/packages/types/src/__tests__/provider-identifiers.test.ts +++ b/packages/types/src/__tests__/provider-identifiers.test.ts @@ -8,7 +8,6 @@ import { isFauxProvider, isInternalProvider, isLocalProvider, - isActiveProviderName, isProviderName, isRetiredProvider, localProviders, @@ -196,22 +195,17 @@ describe("provider identifiers", () => { it("preserves provider-settings type guards", () => { for (const identifier of expectedProviderIdentifiers) { - expect(isActiveProviderName(identifier)).toBe(true) + expect(isProviderName(identifier)).toBe(true) expect(isRetiredProvider(identifier)).toBe(false) } for (const identifier of expectedRetiredProviderIdentifiers) { - expect(isActiveProviderName(identifier)).toBe(false) + expect(isProviderName(identifier)).toBe(false) expect(isRetiredProvider(identifier)).toBe(true) } - expect(isActiveProviderName("unknown-provider")).toBe(false) - expect(isActiveProviderName(undefined)).toBe(false) - expect(isRetiredProvider("unknown-provider")).toBe(false) - }) - - it("preserves the deprecated isProviderName compatibility export", () => { - expect(isProviderName(providerIdentifiers.anthropic)).toBe(true) expect(isProviderName("unknown-provider")).toBe(false) + expect(isProviderName(undefined)).toBe(false) + expect(isRetiredProvider("unknown-provider")).toBe(false) }) }) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 2a3486bd08..b96a3f33b6 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -134,14 +134,9 @@ export const providerNamesSchema = z.enum(providerNames) export type ProviderName = z.infer -export const isActiveProviderName = (key: unknown): key is ProviderName => +export const isProviderName = (key: unknown): key is ProviderName => typeof key === "string" && providerNames.includes(key as ProviderName) -/** - * @deprecated Use `isActiveProviderName()` instead. - */ -export const isProviderName = isActiveProviderName - /** * RetiredProviderName */ @@ -805,7 +800,7 @@ export type TypicalProvider = Exclude - isActiveProviderName(key) && !isInternalProvider(key) && !isCustomProvider(key) && !isFauxProvider(key) + isProviderName(key) && !isInternalProvider(key) && !isCustomProvider(key) && !isFauxProvider(key) /** * @deprecated Use `getModelId()` instead. This map is retained for API compatibility. @@ -825,7 +820,7 @@ export const modelIdKeysByProvider = Object.fromEntries( ) as Record export function getModelId(settings: ProviderSettings): string | undefined { - if (isActiveProviderName(settings.apiProvider)) { + if (isProviderName(settings.apiProvider)) { return providerDefinitions[settings.apiProvider]?.getModelId(settings) } diff --git a/src/core/config/ContextProxy.ts b/src/core/config/ContextProxy.ts index a9f25491b3..97d4104afc 100644 --- a/src/core/config/ContextProxy.ts +++ b/src/core/config/ContextProxy.ts @@ -15,7 +15,7 @@ import { providerSettingsSchema, globalSettingsSchema, isSecretStateKey, - isActiveProviderName, + isProviderName, isRetiredProvider, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" @@ -262,7 +262,7 @@ export class ContextProxy { try { const apiProvider = this.stateCache.apiProvider const isKnownProvider = - typeof apiProvider === "string" && (isActiveProviderName(apiProvider) || isRetiredProvider(apiProvider)) + typeof apiProvider === "string" && (isProviderName(apiProvider) || isRetiredProvider(apiProvider)) if (apiProvider !== undefined && !isKnownProvider) { logger.info(`[ContextProxy] Found invalid provider "${apiProvider}" in storage - clearing it`) @@ -494,7 +494,7 @@ export class ContextProxy { const isKnownProvider = typeof sanitizedValues.apiProvider === "string" && - (isActiveProviderName(sanitizedValues.apiProvider) || isRetiredProvider(sanitizedValues.apiProvider)) + (isProviderName(sanitizedValues.apiProvider) || isRetiredProvider(sanitizedValues.apiProvider)) if (sanitizedValues.apiProvider !== undefined && !isKnownProvider) { logger.info( diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index efb240037b..51f79cff35 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -11,7 +11,7 @@ import { DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, getModelId, type ProviderName, - isActiveProviderName, + isProviderName, isRetiredProvider, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" @@ -688,7 +688,7 @@ export class ProviderSettingsManager { // Check if apiProvider is set and if it's still recognized (active or retired) if ( apiProvider !== undefined && - (typeof apiProvider !== "string" || (!isActiveProviderName(apiProvider) && !isRetiredProvider(apiProvider))) + (typeof apiProvider !== "string" || (!isProviderName(apiProvider) && !isRetiredProvider(apiProvider))) ) { console.log( `[ProviderSettingsManager] Sanitizing unknown provider "${config.apiProvider}" - resetting to undefined`, diff --git a/src/core/config/importExport.ts b/src/core/config/importExport.ts index c819a9aaf1..b5fd5fdf98 100644 --- a/src/core/config/importExport.ts +++ b/src/core/config/importExport.ts @@ -9,7 +9,7 @@ import { z, ZodError } from "zod" import { globalSettingsSchema, providerSettingsWithIdSchema, - isActiveProviderName, + isProviderName, type GlobalSettings, type ProviderSettingsWithId, } from "@roo-code/types" @@ -58,7 +58,7 @@ function sanitizeProviderConfig(configName: string, apiConfig: unknown): { confi } // Check if apiProvider is set and if it's still valid - if (config.apiProvider !== undefined && !isActiveProviderName(config.apiProvider)) { + if (config.apiProvider !== undefined && !isProviderName(config.apiProvider)) { const invalidProvider = config.apiProvider // Return a new config object without the invalid apiProvider const { apiProvider, ...restConfig } = config From 487270b6a2259e44d4502cebadde7bfb1e3bf13c Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 16:24:24 +0300 Subject: [PATCH 10/16] test(types): clarify provider model ID selection --- .../src/__tests__/provider-model-id.test.ts | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/packages/types/src/__tests__/provider-model-id.test.ts b/packages/types/src/__tests__/provider-model-id.test.ts index 5a6e40546e..9e26713787 100644 --- a/packages/types/src/__tests__/provider-model-id.test.ts +++ b/packages/types/src/__tests__/provider-model-id.test.ts @@ -1,6 +1,6 @@ import { getModelId, modelIdKeys, providerIdentifiers, type ProviderSettings } from "../index.js" -type ExpectedModelIdKeys = readonly [ +const expectedModelIdKeys = [ "apiModelId", "openRouterModelId", "openAiModelId", @@ -14,33 +14,21 @@ type ExpectedModelIdKeys = readonly [ "opencodeGoModelId", "kenariModelId", "zooGatewayModelId", -] +] as const + +type ExpectedModelIdKeys = typeof expectedModelIdKeys const exactModelIdKeys: ExpectedModelIdKeys = modelIdKeys void exactModelIdKeys describe("modelIdKeys", () => { it("preserves every model ID setting for compatibility", () => { - expect(modelIdKeys).toEqual([ - "apiModelId", - "openRouterModelId", - "openAiModelId", - "ollamaModelId", - "lmStudioModelId", - "lmStudioDraftModelId", - "requestyModelId", - "unboundModelId", - "litellmModelId", - "vercelAiGatewayModelId", - "opencodeGoModelId", - "kenariModelId", - "zooGatewayModelId", - ]) + expect(modelIdKeys).toEqual(expectedModelIdKeys) }) }) describe("getModelId", () => { - it("uses the provider-specific model ID field", () => { + it("uses a provider-specific model ID field instead of the shared apiModelId field", () => { const settings: ProviderSettings = { apiProvider: providerIdentifiers.openrouter, apiModelId: "unrelated-model", @@ -50,10 +38,10 @@ describe("getModelId", () => { expect(getModelId(settings)).toBe("openrouter-model") }) - it("uses the active provider when other model ID fields are present", () => { + it("selects the active provider's field when multiple provider-specific model IDs are present", () => { const settings: ProviderSettings = { apiProvider: providerIdentifiers.ollama, - apiModelId: "anthropic-model", + openRouterModelId: "inactive-openrouter-model", ollamaModelId: "ollama-model", } From 3027707b7e22e45dd81c714b84f3727036e95bec Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 16:29:22 +0300 Subject: [PATCH 11/16] test(types): simplify model ID key contract --- packages/types/src/__tests__/provider-model-id.test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/types/src/__tests__/provider-model-id.test.ts b/packages/types/src/__tests__/provider-model-id.test.ts index 9e26713787..bec5425b54 100644 --- a/packages/types/src/__tests__/provider-model-id.test.ts +++ b/packages/types/src/__tests__/provider-model-id.test.ts @@ -16,13 +16,8 @@ const expectedModelIdKeys = [ "zooGatewayModelId", ] as const -type ExpectedModelIdKeys = typeof expectedModelIdKeys - -const exactModelIdKeys: ExpectedModelIdKeys = modelIdKeys -void exactModelIdKeys - describe("modelIdKeys", () => { - it("preserves every model ID setting for compatibility", () => { + it("preserves every model ID setting and its legacy precedence order", () => { expect(modelIdKeys).toEqual(expectedModelIdKeys) }) }) From f507d72991f40298e64edc92acfe2945a9c68636 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 17:05:24 +0300 Subject: [PATCH 12/16] refactor(types): make provider model accessors explicit --- packages/types/src/provider-settings.ts | 69 +++++++++++++++++++++---- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index b96a3f33b6..3993935353 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -205,21 +205,37 @@ const baseProviderSettingsShape = { verbosity: verbosityLevelsSchema.optional(), } -type ProviderModelIdAccessor = (settings: Record) => string | undefined +type ModelId = string | undefined +type UntypedProviderSettings = Record +type ProviderModelIdAccessor = (settings: UntypedProviderSettings) => ModelId -type TypedProviderModelIdAccessor = (settings: z.infer>) => string | undefined +type ProviderSettingsFromSchema = z.infer> + +type TypedProviderModelIdAccessor = (settings: ProviderSettingsFromSchema) => ModelId + +type ProviderDefinitionInput

= { + apiProvider: P + schema: S + modelIdKey?: Extract + getModelId: TypedProviderModelIdAccessor +} + +const createModelIdAccessor = + (modelIdKey: string): ProviderModelIdAccessor => + (settings) => + settings[modelIdKey] as ModelId // `modelIdKey` supports deprecated exports. Remove it in favor of an accessor-only contract when those exports are removed. -const createProviderDefinition =

>({ +const createProviderDefinition =

({ apiProvider, schema, ...modelIdDefinition -}: { - apiProvider: P - schema: S -} & ({ modelIdKey: K; getModelId?: never } | { modelIdKey?: never; getModelId: TypedProviderModelIdAccessor })) => { - const getModelId: TypedProviderModelIdAccessor = - modelIdDefinition.getModelId ?? ((settings) => settings[modelIdDefinition.modelIdKey] as string | undefined) +}: ProviderDefinitionInput) => { + const settingsSchema = z.object(schema) + const getModelId: ProviderModelIdAccessor = (settings) => { + const parsedSettings = settingsSchema.safeParse(settings) + return parsedSettings.success ? modelIdDefinition.getModelId(parsedSettings.data) : undefined + } return { apiProvider, @@ -229,7 +245,7 @@ const createProviderDefinition =

getModelId(settings as z.infer>)) satisfies ProviderModelIdAccessor, + getModelId, } } @@ -242,6 +258,7 @@ const apiModelIdProviderModelShape = { const anthropicProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.anthropic, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, apiKey: z.string().optional(), @@ -254,6 +271,7 @@ const anthropicProviderDefinition = createProviderDefinition({ const openRouterProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openrouter, modelIdKey: OPEN_ROUTER_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OPEN_ROUTER_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, openRouterApiKey: z.string().optional(), @@ -266,6 +284,7 @@ const openRouterProviderDefinition = createProviderDefinition({ const bedrockProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.bedrock, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, awsAccessKey: z.string().optional(), @@ -291,6 +310,7 @@ const bedrockProviderDefinition = createProviderDefinition({ const vertexProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.vertex, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, vertexKeyFile: z.string().optional(), @@ -304,6 +324,7 @@ const vertexProviderDefinition = createProviderDefinition({ const openAiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openai, modelIdKey: OPEN_AI_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OPEN_AI_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, openAiBaseUrl: z.string().optional(), @@ -322,6 +343,7 @@ const openAiProviderDefinition = createProviderDefinition({ const ollamaProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.ollama, modelIdKey: OLLAMA_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OLLAMA_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, [OLLAMA_MODEL_ID_FIELD]: z.string().optional(), @@ -350,6 +372,7 @@ const vsCodeLmProviderDefinition = createProviderDefinition({ const lmStudioProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.lmstudio, modelIdKey: LM_STUDIO_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(LM_STUDIO_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, [LM_STUDIO_MODEL_ID_FIELD]: z.string().optional(), @@ -362,6 +385,7 @@ const lmStudioProviderDefinition = createProviderDefinition({ const geminiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.gemini, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, geminiApiKey: z.string().optional(), @@ -372,6 +396,7 @@ const geminiProviderDefinition = createProviderDefinition({ const geminiCliProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.geminiCli, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, geminiCliOAuthPath: z.string().optional(), @@ -382,6 +407,7 @@ const geminiCliProviderDefinition = createProviderDefinition({ const openAiCodexProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openaiCodex, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, // Codex "Fast" mode maps to the Responses API priority service tier. @@ -392,6 +418,7 @@ const openAiCodexProviderDefinition = createProviderDefinition({ const openAiNativeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openaiNative, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, openAiNativeApiKey: z.string().optional(), @@ -405,6 +432,7 @@ const openAiNativeProviderDefinition = createProviderDefinition({ const mistralProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.mistral, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, mistralApiKey: z.string().optional(), @@ -415,6 +443,7 @@ const mistralProviderDefinition = createProviderDefinition({ const deepSeekProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.deepseek, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, deepSeekBaseUrl: z.string().optional(), @@ -425,6 +454,7 @@ const deepSeekProviderDefinition = createProviderDefinition({ const poeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.poe, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, poeApiKey: z.string().optional(), @@ -435,6 +465,7 @@ const poeProviderDefinition = createProviderDefinition({ const moonshotProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.moonshot, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, moonshotBaseUrl: z @@ -450,6 +481,7 @@ export type KimiCodeAuthMethod = z.infer const kimiCodeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.kimiCode, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, kimiCodeAuthMethod: kimiCodeAuthMethodSchema.optional(), @@ -460,6 +492,7 @@ const kimiCodeProviderDefinition = createProviderDefinition({ const minimaxProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.minimax, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, minimaxBaseUrl: z @@ -472,6 +505,7 @@ const minimaxProviderDefinition = createProviderDefinition({ const mimoProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.mimo, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, mimoBaseUrl: z @@ -489,6 +523,7 @@ const mimoProviderDefinition = createProviderDefinition({ const requestyProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.requesty, modelIdKey: REQUESTY_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(REQUESTY_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, requestyBaseUrl: z.string().optional(), @@ -500,6 +535,7 @@ const requestyProviderDefinition = createProviderDefinition({ const unboundProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.unbound, modelIdKey: UNBOUND_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(UNBOUND_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, unboundApiKey: z.string().optional(), @@ -510,6 +546,7 @@ const unboundProviderDefinition = createProviderDefinition({ const fakeAiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.fakeAi, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, fakeAi: z.unknown().optional(), @@ -519,6 +556,7 @@ const fakeAiProviderDefinition = createProviderDefinition({ const xaiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.xai, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, xaiApiKey: z.string().optional(), @@ -528,6 +566,7 @@ const xaiProviderDefinition = createProviderDefinition({ const litellmProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.litellm, modelIdKey: LITELLM_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(LITELLM_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, litellmBaseUrl: z.string().optional(), @@ -540,6 +579,7 @@ const litellmProviderDefinition = createProviderDefinition({ const sambaNovaProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.sambanova, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, sambaNovaApiKey: z.string().optional(), @@ -553,6 +593,7 @@ export type ZaiApiLine = z.infer const zaiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.zai, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, zaiApiKey: z.string().optional(), @@ -563,6 +604,7 @@ const zaiProviderDefinition = createProviderDefinition({ const fireworksProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.fireworks, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, fireworksApiKey: z.string().optional(), @@ -572,6 +614,7 @@ const fireworksProviderDefinition = createProviderDefinition({ const friendliProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.friendli, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, friendliApiKey: z.string().optional(), @@ -581,6 +624,7 @@ const friendliProviderDefinition = createProviderDefinition({ const qwenCodeProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.qwenCode, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, qwenCodeOauthPath: z.string().optional(), @@ -590,6 +634,7 @@ const qwenCodeProviderDefinition = createProviderDefinition({ const vercelAiGatewayProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.vercelAiGateway, modelIdKey: VERCEL_AI_GATEWAY_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(VERCEL_AI_GATEWAY_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, vercelAiGatewayApiKey: z.string().optional(), @@ -600,6 +645,7 @@ const vercelAiGatewayProviderDefinition = createProviderDefinition({ const opencodeGoProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.opencodeGo, modelIdKey: OPENCODE_GO_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OPENCODE_GO_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, opencodeGoApiKey: z.string().optional(), @@ -612,6 +658,7 @@ const KENARI_MODEL_ID_FIELD = "kenariModelId" const kenariProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.kenari, modelIdKey: KENARI_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(KENARI_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, kenariApiKey: z.string().optional(), @@ -622,6 +669,7 @@ const kenariProviderDefinition = createProviderDefinition({ const zooGatewayProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.zooGateway, modelIdKey: ZOO_GATEWAY_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(ZOO_GATEWAY_MODEL_ID_FIELD), schema: { ...baseProviderSettingsShape, zooSessionToken: z.string().optional(), @@ -633,6 +681,7 @@ const zooGatewayProviderDefinition = createProviderDefinition({ const basetenProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.baseten, modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), schema: { ...apiModelIdProviderModelShape, basetenApiKey: z.string().optional(), From 46a558655a7cd79ff6c926531303f32a37a4e052 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 17:15:01 +0300 Subject: [PATCH 13/16] refactor(types): decompose provider settings --- .../src/__tests__/provider-settings.test.ts | 8 + packages/types/src/provider-settings.ts | 577 +----------------- .../types/src/provider-settings/anthropic.ts | 22 + .../types/src/provider-settings/baseten.ts | 19 + .../types/src/provider-settings/bedrock.ts | 35 ++ .../types/src/provider-settings/common.ts | 87 +++ .../types/src/provider-settings/deepseek.ts | 20 + .../types/src/provider-settings/fake-ai.ts | 19 + .../types/src/provider-settings/fireworks.ts | 19 + .../types/src/provider-settings/friendli.ts | 19 + .../types/src/provider-settings/gemini-cli.ts | 20 + .../types/src/provider-settings/gemini.ts | 20 + packages/types/src/provider-settings/index.ts | 78 +++ .../types/src/provider-settings/kenari.ts | 17 + .../types/src/provider-settings/kimi-code.ts | 23 + .../types/src/provider-settings/litellm.ts | 22 + .../types/src/provider-settings/lm-studio.ts | 22 + packages/types/src/provider-settings/mimo.ts | 27 + .../types/src/provider-settings/minimax.ts | 22 + .../types/src/provider-settings/mistral.ts | 20 + .../types/src/provider-settings/moonshot.ts | 25 + .../types/src/provider-settings/ollama.ts | 22 + .../src/provider-settings/openai-codex.ts | 20 + .../src/provider-settings/openai-native.ts | 24 + .../types/src/provider-settings/openai.ts | 29 + .../src/provider-settings/opencode-go.ts | 20 + .../types/src/provider-settings/openrouter.ts | 22 + packages/types/src/provider-settings/poe.ts | 20 + .../types/src/provider-settings/qwen-code.ts | 19 + .../types/src/provider-settings/requesty.ts | 21 + .../types/src/provider-settings/sambanova.ts | 23 + .../types/src/provider-settings/unbound.ts | 20 + .../provider-settings/vercel-ai-gateway.ts | 20 + .../types/src/provider-settings/vertex.ts | 23 + .../types/src/provider-settings/vscode-lm.ts | 20 + packages/types/src/provider-settings/xai.ts | 19 + packages/types/src/provider-settings/zai.ts | 23 + .../src/provider-settings/zoo-gateway.ts | 21 + 38 files changed, 920 insertions(+), 567 deletions(-) create mode 100644 packages/types/src/provider-settings/anthropic.ts create mode 100644 packages/types/src/provider-settings/baseten.ts create mode 100644 packages/types/src/provider-settings/bedrock.ts create mode 100644 packages/types/src/provider-settings/common.ts create mode 100644 packages/types/src/provider-settings/deepseek.ts create mode 100644 packages/types/src/provider-settings/fake-ai.ts create mode 100644 packages/types/src/provider-settings/fireworks.ts create mode 100644 packages/types/src/provider-settings/friendli.ts create mode 100644 packages/types/src/provider-settings/gemini-cli.ts create mode 100644 packages/types/src/provider-settings/gemini.ts create mode 100644 packages/types/src/provider-settings/index.ts create mode 100644 packages/types/src/provider-settings/kenari.ts create mode 100644 packages/types/src/provider-settings/kimi-code.ts create mode 100644 packages/types/src/provider-settings/litellm.ts create mode 100644 packages/types/src/provider-settings/lm-studio.ts create mode 100644 packages/types/src/provider-settings/mimo.ts create mode 100644 packages/types/src/provider-settings/minimax.ts create mode 100644 packages/types/src/provider-settings/mistral.ts create mode 100644 packages/types/src/provider-settings/moonshot.ts create mode 100644 packages/types/src/provider-settings/ollama.ts create mode 100644 packages/types/src/provider-settings/openai-codex.ts create mode 100644 packages/types/src/provider-settings/openai-native.ts create mode 100644 packages/types/src/provider-settings/openai.ts create mode 100644 packages/types/src/provider-settings/opencode-go.ts create mode 100644 packages/types/src/provider-settings/openrouter.ts create mode 100644 packages/types/src/provider-settings/poe.ts create mode 100644 packages/types/src/provider-settings/qwen-code.ts create mode 100644 packages/types/src/provider-settings/requesty.ts create mode 100644 packages/types/src/provider-settings/sambanova.ts create mode 100644 packages/types/src/provider-settings/unbound.ts create mode 100644 packages/types/src/provider-settings/vercel-ai-gateway.ts create mode 100644 packages/types/src/provider-settings/vertex.ts create mode 100644 packages/types/src/provider-settings/vscode-lm.ts create mode 100644 packages/types/src/provider-settings/xai.ts create mode 100644 packages/types/src/provider-settings/zai.ts create mode 100644 packages/types/src/provider-settings/zoo-gateway.ts diff --git a/packages/types/src/__tests__/provider-settings.test.ts b/packages/types/src/__tests__/provider-settings.test.ts index 98471809b3..33fa4e3aac 100644 --- a/packages/types/src/__tests__/provider-settings.test.ts +++ b/packages/types/src/__tests__/provider-settings.test.ts @@ -7,8 +7,16 @@ import { providerSettingsSchemaDiscriminated, } from "../provider-settings.js" import { OpenAiCodexServiceTier, OpenAiServiceTier } from "../model.js" +import { providerDefinitionList } from "../provider-settings/index.js" describe("provider settings discriminated union", () => { + it("composes exactly one provider-specific definition for every provider", () => { + const registeredProviders = providerDefinitionList.map(({ apiProvider }) => apiProvider) + + expect([...registeredProviders].sort()).toEqual([...providerNames].sort()) + expect(new Set(registeredProviders).size).toBe(providerNames.length) + }) + it.each(providerNames)("accepts the %s provider branch", (apiProvider) => { expect(providerSettingsSchemaDiscriminated.safeParse({ apiProvider }).success).toBe(true) }) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 3993935353..547abca6dc 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -1,12 +1,15 @@ import { z } from "zod" -import { - modelInfoSchema, - openAiCodexServiceTierSchema, - reasoningEffortSettingSchema, - verbosityLevelsSchema, - serviceTierSchema, -} from "./model.js" +import { providerDefinitionList, type ProviderDefinition } from "./provider-settings/index.js" +import { API_PROVIDER_FIELD, SETTINGS_SHAPE_FIELD } from "./provider-settings/common.js" +export { + OPEN_AI_CODEX_SERVICE_TIER_KEY, + kimiCodeAuthMethodSchema, + type KimiCodeAuthMethod, + zaiApiLineSchema, + type ZaiApiLine, +} from "./provider-settings/index.js" + import { codebaseIndexProviderSchema } from "./codebase-index.js" import type { UnionToIntersection } from "./type-fu.js" import { @@ -45,7 +48,6 @@ import { */ export const DEFAULT_CONSECUTIVE_MISTAKE_LIMIT = 3 -export const OPEN_AI_CODEX_SERVICE_TIER_KEY = "openAiCodexServiceTier" /** * DynamicProvider @@ -161,20 +163,6 @@ export type ProviderNameWithRetired = z.infer * ProviderSettings */ -const baseProviderSettingsShape = { - includeMaxTokens: z.boolean().optional(), - todoListEnabled: z.boolean().optional(), - modelTemperature: z.number().nullish(), - rateLimitSeconds: z.number().optional(), - consecutiveMistakeLimit: z.number().min(0).optional(), - - // Model reasoning. - enableReasoningEffort: z.boolean().optional(), - reasoningEffort: reasoningEffortSettingSchema.optional(), - modelMaxTokens: z.number().optional(), - modelMaxThinkingTokens: z.number().optional(), - - // Model verbosity. - verbosity: verbosityLevelsSchema.optional(), -} - -type ModelId = string | undefined -type UntypedProviderSettings = Record -type ProviderModelIdAccessor = (settings: UntypedProviderSettings) => ModelId - -type ProviderSettingsFromSchema = z.infer> - -type TypedProviderModelIdAccessor = (settings: ProviderSettingsFromSchema) => ModelId - -type ProviderDefinitionInput

= { - apiProvider: P - schema: S - modelIdKey?: Extract - getModelId: TypedProviderModelIdAccessor -} - -const createModelIdAccessor = - (modelIdKey: string): ProviderModelIdAccessor => - (settings) => - settings[modelIdKey] as ModelId - -// `modelIdKey` supports deprecated exports. Remove it in favor of an accessor-only contract when those exports are removed. -const createProviderDefinition =

({ - apiProvider, - schema, - ...modelIdDefinition -}: ProviderDefinitionInput) => { - const settingsSchema = z.object(schema) - const getModelId: ProviderModelIdAccessor = (settings) => { - const parsedSettings = settingsSchema.safeParse(settings) - return parsedSettings.success ? modelIdDefinition.getModelId(parsedSettings.data) : undefined - } - - return { - apiProvider, - settingsShape: schema, - modelIdKey: modelIdDefinition.modelIdKey, - schema: z.object({ - ...schema, - [API_PROVIDER_FIELD]: z.literal(apiProvider), - }), - getModelId, - } -} - -// Several of the providers share common model config properties. -const apiModelIdProviderModelShape = { - ...baseProviderSettingsShape, - [API_MODEL_ID_FIELD]: z.string().optional(), -} - -const anthropicProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.anthropic, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - apiKey: z.string().optional(), - anthropicBaseUrl: z.string().optional(), - anthropicUseAuthToken: z.boolean().optional(), - anthropicBeta1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. - }, -}) - -const openRouterProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.openrouter, - modelIdKey: OPEN_ROUTER_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(OPEN_ROUTER_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - openRouterApiKey: z.string().optional(), - [OPEN_ROUTER_MODEL_ID_FIELD]: z.string().optional(), - openRouterBaseUrl: z.string().optional(), - openRouterSpecificProvider: z.string().optional(), - }, -}) - -const bedrockProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.bedrock, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - awsAccessKey: z.string().optional(), - awsSecretKey: z.string().optional(), - awsSessionToken: z.string().optional(), - awsRegion: z.string().optional(), - awsUseCrossRegionInference: z.boolean().optional(), - awsUseGlobalInference: z.boolean().optional(), // Enable Global Inference profile routing when supported - awsUsePromptCache: z.boolean().optional(), - awsProfile: z.string().optional(), - awsUseProfile: z.boolean().optional(), - awsApiKey: z.string().optional(), - awsUseApiKey: z.boolean().optional(), - awsCustomArn: z.string().optional(), - awsModelContextWindow: z.number().optional(), - awsBedrockEndpointEnabled: z.boolean().optional(), - awsBedrockEndpoint: z.string().optional(), - awsBedrock1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. - awsBedrockServiceTier: z.enum(["STANDARD", "FLEX", "PRIORITY"]).optional(), // AWS Bedrock service tier selection - }, -}) - -const vertexProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.vertex, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - vertexKeyFile: z.string().optional(), - vertexJsonCredentials: z.string().optional(), - vertexProjectId: z.string().optional(), - vertexRegion: z.string().optional(), - vertex1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. - }, -}) - -const openAiProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.openai, - modelIdKey: OPEN_AI_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(OPEN_AI_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - openAiBaseUrl: z.string().optional(), - openAiApiKey: z.string().optional(), - openAiR1FormatEnabled: z.boolean().optional(), - [OPEN_AI_MODEL_ID_FIELD]: z.string().optional(), - openAiCustomModelInfo: modelInfoSchema.nullish(), - openAiUseAzure: z.boolean().optional(), - azureApiVersion: z.string().optional(), - openAiStreamingEnabled: z.boolean().optional(), - openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. - openAiHeaders: z.record(z.string(), z.string()).optional(), - }, -}) - -const ollamaProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.ollama, - modelIdKey: OLLAMA_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(OLLAMA_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - [OLLAMA_MODEL_ID_FIELD]: z.string().optional(), - ollamaBaseUrl: z.string().optional(), - ollamaApiKey: z.string().optional(), - ollamaNumCtx: z.number().int().min(128).optional(), - }, -}) - -const vsCodeLmProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.vscodeLm, - getModelId: (settings) => settings.vsCodeLmModelSelector?.id, - schema: { - ...baseProviderSettingsShape, - vsCodeLmModelSelector: z - .object({ - vendor: z.string().optional(), - family: z.string().optional(), - version: z.string().optional(), - id: z.string().optional(), - }) - .optional(), - }, -}) - -const lmStudioProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.lmstudio, - modelIdKey: LM_STUDIO_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(LM_STUDIO_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - [LM_STUDIO_MODEL_ID_FIELD]: z.string().optional(), - lmStudioBaseUrl: z.string().optional(), - lmStudioDraftModelId: z.string().optional(), - lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), - }, -}) - -const geminiProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.gemini, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - geminiApiKey: z.string().optional(), - googleGeminiBaseUrl: z.string().optional(), - }, -}) - -const geminiCliProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.geminiCli, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - geminiCliOAuthPath: z.string().optional(), - geminiCliProjectId: z.string().optional(), - }, -}) - -const openAiCodexProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.openaiCodex, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - // Codex "Fast" mode maps to the Responses API priority service tier. - [OPEN_AI_CODEX_SERVICE_TIER_KEY]: openAiCodexServiceTierSchema.optional(), - }, -}) - -const openAiNativeProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.openaiNative, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - openAiNativeApiKey: z.string().optional(), - openAiNativeBaseUrl: z.string().optional(), - // OpenAI Responses API service tier for openai-native provider only. - // UI should only expose this when the selected model supports flex/priority. - openAiNativeServiceTier: serviceTierSchema.optional(), - }, -}) - -const mistralProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.mistral, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - mistralApiKey: z.string().optional(), - mistralCodestralUrl: z.string().optional(), - }, -}) - -const deepSeekProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.deepseek, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - deepSeekBaseUrl: z.string().optional(), - deepSeekApiKey: z.string().optional(), - }, -}) - -const poeProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.poe, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - poeApiKey: z.string().optional(), - poeBaseUrl: z.string().optional(), - }, -}) - -const moonshotProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.moonshot, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - moonshotBaseUrl: z - .union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")]) - .optional(), - moonshotApiKey: z.string().optional(), - }, -}) - -export const kimiCodeAuthMethodSchema = z.enum(["oauth", "api-key"]) -export type KimiCodeAuthMethod = z.infer - -const kimiCodeProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.kimiCode, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - kimiCodeAuthMethod: kimiCodeAuthMethodSchema.optional(), - kimiCodeApiKey: z.string().optional(), - }, -}) - -const minimaxProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.minimax, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - minimaxBaseUrl: z - .union([z.literal("https://api.minimax.io/v1"), z.literal("https://api.minimaxi.com/v1")]) - .optional(), - minimaxApiKey: z.string().optional(), - }, -}) - -const mimoProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.mimo, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - mimoBaseUrl: z - .union([ - z.literal("https://api.xiaomimimo.com/v1"), - z.literal("https://token-plan-cn.xiaomimimo.com/v1"), - z.literal("https://token-plan-sgp.xiaomimimo.com/v1"), - z.literal("https://token-plan-ams.xiaomimimo.com/v1"), - ]) - .optional(), - mimoApiKey: z.string().optional(), - }, -}) - -const requestyProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.requesty, - modelIdKey: REQUESTY_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(REQUESTY_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - requestyBaseUrl: z.string().optional(), - requestyApiKey: z.string().optional(), - [REQUESTY_MODEL_ID_FIELD]: z.string().optional(), - }, -}) - -const unboundProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.unbound, - modelIdKey: UNBOUND_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(UNBOUND_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - unboundApiKey: z.string().optional(), - [UNBOUND_MODEL_ID_FIELD]: z.string().optional(), - }, -}) - -const fakeAiProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.fakeAi, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - fakeAi: z.unknown().optional(), - }, -}) - -const xaiProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.xai, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - xaiApiKey: z.string().optional(), - }, -}) - -const litellmProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.litellm, - modelIdKey: LITELLM_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(LITELLM_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - litellmBaseUrl: z.string().optional(), - litellmApiKey: z.string().optional(), - [LITELLM_MODEL_ID_FIELD]: z.string().optional(), - litellmUsePromptCache: z.boolean().optional(), - }, -}) - -const sambaNovaProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.sambanova, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - sambaNovaApiKey: z.string().optional(), - }, -}) - -export const zaiApiLineSchema = z.enum(["international_coding", "china_coding", "international_api", "china_api"]) - -export type ZaiApiLine = z.infer - -const zaiProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.zai, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - zaiApiKey: z.string().optional(), - zaiApiLine: zaiApiLineSchema.optional(), - }, -}) - -const fireworksProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.fireworks, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - fireworksApiKey: z.string().optional(), - }, -}) - -const friendliProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.friendli, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - friendliApiKey: z.string().optional(), - }, -}) - -const qwenCodeProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.qwenCode, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - qwenCodeOauthPath: z.string().optional(), - }, -}) - -const vercelAiGatewayProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.vercelAiGateway, - modelIdKey: VERCEL_AI_GATEWAY_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(VERCEL_AI_GATEWAY_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - vercelAiGatewayApiKey: z.string().optional(), - [VERCEL_AI_GATEWAY_MODEL_ID_FIELD]: z.string().optional(), - }, -}) - -const opencodeGoProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.opencodeGo, - modelIdKey: OPENCODE_GO_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(OPENCODE_GO_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - opencodeGoApiKey: z.string().optional(), - [OPENCODE_GO_MODEL_ID_FIELD]: z.string().optional(), - }, -}) - -const KENARI_MODEL_ID_FIELD = "kenariModelId" - -const kenariProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.kenari, - modelIdKey: KENARI_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(KENARI_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - kenariApiKey: z.string().optional(), - [KENARI_MODEL_ID_FIELD]: z.string().optional(), - }, -}) - -const zooGatewayProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.zooGateway, - modelIdKey: ZOO_GATEWAY_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(ZOO_GATEWAY_MODEL_ID_FIELD), - schema: { - ...baseProviderSettingsShape, - zooSessionToken: z.string().optional(), - [ZOO_GATEWAY_MODEL_ID_FIELD]: z.string().optional(), - zooGatewayBaseUrl: z.string().optional(), - }, -}) - -const basetenProviderDefinition = createProviderDefinition({ - apiProvider: providerIdentifiers.baseten, - modelIdKey: API_MODEL_ID_FIELD, - getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), - schema: { - ...apiModelIdProviderModelShape, - basetenApiKey: z.string().optional(), - }, -}) - -type ProviderDefinition = { - apiProvider: ProviderName - settingsShape: z.ZodRawShape - modelIdKey?: string - schema: z.ZodDiscriminatedUnionOption - getModelId: ProviderModelIdAccessor -} - -const providerDefinitionList = [ - anthropicProviderDefinition, - openRouterProviderDefinition, - bedrockProviderDefinition, - vertexProviderDefinition, - openAiProviderDefinition, - ollamaProviderDefinition, - vsCodeLmProviderDefinition, - lmStudioProviderDefinition, - geminiProviderDefinition, - geminiCliProviderDefinition, - openAiCodexProviderDefinition, - openAiNativeProviderDefinition, - mistralProviderDefinition, - deepSeekProviderDefinition, - poeProviderDefinition, - moonshotProviderDefinition, - kimiCodeProviderDefinition, - minimaxProviderDefinition, - mimoProviderDefinition, - requestyProviderDefinition, - unboundProviderDefinition, - fakeAiProviderDefinition, - xaiProviderDefinition, - basetenProviderDefinition, - litellmProviderDefinition, - sambaNovaProviderDefinition, - zaiProviderDefinition, - fireworksProviderDefinition, - friendliProviderDefinition, - qwenCodeProviderDefinition, - vercelAiGatewayProviderDefinition, - opencodeGoProviderDefinition, - kenariProviderDefinition, - zooGatewayProviderDefinition, -] as const satisfies readonly ProviderDefinition[] - type ListedProvider = (typeof providerDefinitionList)[number][typeof API_PROVIDER_FIELD] const allProvidersAreDefined: Exclude extends never ? true : never = true void allProvidersAreDefined diff --git a/packages/types/src/provider-settings/anthropic.ts b/packages/types/src/provider-settings/anthropic.ts new file mode 100644 index 0000000000..80ff6f15fb --- /dev/null +++ b/packages/types/src/provider-settings/anthropic.ts @@ -0,0 +1,22 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const anthropicProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.anthropic, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + apiKey: z.string().optional(), + anthropicBaseUrl: z.string().optional(), + anthropicUseAuthToken: z.boolean().optional(), + anthropicBeta1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + }, +}) diff --git a/packages/types/src/provider-settings/baseten.ts b/packages/types/src/provider-settings/baseten.ts new file mode 100644 index 0000000000..ac9649af30 --- /dev/null +++ b/packages/types/src/provider-settings/baseten.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const basetenProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.baseten, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + basetenApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/bedrock.ts b/packages/types/src/provider-settings/bedrock.ts new file mode 100644 index 0000000000..0456c7c232 --- /dev/null +++ b/packages/types/src/provider-settings/bedrock.ts @@ -0,0 +1,35 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const bedrockProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.bedrock, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + awsAccessKey: z.string().optional(), + awsSecretKey: z.string().optional(), + awsSessionToken: z.string().optional(), + awsRegion: z.string().optional(), + awsUseCrossRegionInference: z.boolean().optional(), + awsUseGlobalInference: z.boolean().optional(), // Enable Global Inference profile routing when supported + awsUsePromptCache: z.boolean().optional(), + awsProfile: z.string().optional(), + awsUseProfile: z.boolean().optional(), + awsApiKey: z.string().optional(), + awsUseApiKey: z.boolean().optional(), + awsCustomArn: z.string().optional(), + awsModelContextWindow: z.number().optional(), + awsBedrockEndpointEnabled: z.boolean().optional(), + awsBedrockEndpoint: z.string().optional(), + awsBedrock1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + awsBedrockServiceTier: z.enum(["STANDARD", "FLEX", "PRIORITY"]).optional(), // AWS Bedrock service tier selection + }, +}) diff --git a/packages/types/src/provider-settings/common.ts b/packages/types/src/provider-settings/common.ts new file mode 100644 index 0000000000..18e595e7a8 --- /dev/null +++ b/packages/types/src/provider-settings/common.ts @@ -0,0 +1,87 @@ +import { z } from "zod" + +import { reasoningEffortSettingSchema, verbosityLevelsSchema } from "../model.js" +import type { ProviderIdentifier } from "../provider-identifiers.js" + +export const API_PROVIDER_FIELD = "apiProvider" +export const SETTINGS_SHAPE_FIELD = "settingsShape" +export const API_MODEL_ID_FIELD = "apiModelId" +export const OPEN_ROUTER_MODEL_ID_FIELD = "openRouterModelId" +export const OPEN_AI_MODEL_ID_FIELD = "openAiModelId" +export const OLLAMA_MODEL_ID_FIELD = "ollamaModelId" +export const LM_STUDIO_MODEL_ID_FIELD = "lmStudioModelId" +export const REQUESTY_MODEL_ID_FIELD = "requestyModelId" +export const UNBOUND_MODEL_ID_FIELD = "unboundModelId" +export const LITELLM_MODEL_ID_FIELD = "litellmModelId" +export const VERCEL_AI_GATEWAY_MODEL_ID_FIELD = "vercelAiGatewayModelId" +export const OPENCODE_GO_MODEL_ID_FIELD = "opencodeGoModelId" +export const ZOO_GATEWAY_MODEL_ID_FIELD = "zooGatewayModelId" +export const OPEN_AI_CODEX_SERVICE_TIER_KEY = "openAiCodexServiceTier" + +export const baseProviderSettingsShape = { + includeMaxTokens: z.boolean().optional(), + todoListEnabled: z.boolean().optional(), + modelTemperature: z.number().nullish(), + rateLimitSeconds: z.number().optional(), + consecutiveMistakeLimit: z.number().min(0).optional(), + enableReasoningEffort: z.boolean().optional(), + reasoningEffort: reasoningEffortSettingSchema.optional(), + modelMaxTokens: z.number().optional(), + modelMaxThinkingTokens: z.number().optional(), + verbosity: verbosityLevelsSchema.optional(), +} + +export const apiModelIdProviderModelShape = { + ...baseProviderSettingsShape, + [API_MODEL_ID_FIELD]: z.string().optional(), +} + +type ModelId = string | undefined +type UntypedProviderSettings = Record +type ProviderModelIdAccessor = (settings: UntypedProviderSettings) => ModelId +type ProviderSettingsFromSchema = z.infer> +type TypedProviderModelIdAccessor = (settings: ProviderSettingsFromSchema) => ModelId + +type ProviderDefinitionInput

= { + apiProvider: P + schema: S + modelIdKey?: Extract + getModelId: TypedProviderModelIdAccessor +} + +export type ProviderDefinition = { + apiProvider: ProviderIdentifier + settingsShape: z.ZodRawShape + modelIdKey?: string + schema: z.ZodDiscriminatedUnionOption + getModelId: ProviderModelIdAccessor +} + +export const createModelIdAccessor = + (modelIdKey: string): ProviderModelIdAccessor => + (settings) => + settings[modelIdKey] as ModelId + +// `modelIdKey` supports deprecated exports. Remove it in favor of an accessor-only contract when those exports are removed. +export const createProviderDefinition =

({ + apiProvider, + schema, + ...modelIdDefinition +}: ProviderDefinitionInput) => { + const settingsSchema = z.object(schema) + const getModelId: ProviderModelIdAccessor = (settings) => { + const parsedSettings = settingsSchema.safeParse(settings) + return parsedSettings.success ? modelIdDefinition.getModelId(parsedSettings.data) : undefined + } + + return { + apiProvider, + settingsShape: schema, + modelIdKey: modelIdDefinition.modelIdKey, + schema: z.object({ + ...schema, + [API_PROVIDER_FIELD]: z.literal(apiProvider), + }), + getModelId, + } +} diff --git a/packages/types/src/provider-settings/deepseek.ts b/packages/types/src/provider-settings/deepseek.ts new file mode 100644 index 0000000000..746f87e421 --- /dev/null +++ b/packages/types/src/provider-settings/deepseek.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const deepSeekProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.deepseek, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + deepSeekBaseUrl: z.string().optional(), + deepSeekApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/fake-ai.ts b/packages/types/src/provider-settings/fake-ai.ts new file mode 100644 index 0000000000..f6be6c72ed --- /dev/null +++ b/packages/types/src/provider-settings/fake-ai.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const fakeAiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.fakeAi, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + fakeAi: z.unknown().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/fireworks.ts b/packages/types/src/provider-settings/fireworks.ts new file mode 100644 index 0000000000..8d5e1ef3fa --- /dev/null +++ b/packages/types/src/provider-settings/fireworks.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const fireworksProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.fireworks, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + fireworksApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/friendli.ts b/packages/types/src/provider-settings/friendli.ts new file mode 100644 index 0000000000..c64aa3595b --- /dev/null +++ b/packages/types/src/provider-settings/friendli.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const friendliProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.friendli, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + friendliApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/gemini-cli.ts b/packages/types/src/provider-settings/gemini-cli.ts new file mode 100644 index 0000000000..29cd66d0ec --- /dev/null +++ b/packages/types/src/provider-settings/gemini-cli.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const geminiCliProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.geminiCli, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + geminiCliOAuthPath: z.string().optional(), + geminiCliProjectId: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/gemini.ts b/packages/types/src/provider-settings/gemini.ts new file mode 100644 index 0000000000..fd287e34ac --- /dev/null +++ b/packages/types/src/provider-settings/gemini.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const geminiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.gemini, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + geminiApiKey: z.string().optional(), + googleGeminiBaseUrl: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/index.ts b/packages/types/src/provider-settings/index.ts new file mode 100644 index 0000000000..ce291ac155 --- /dev/null +++ b/packages/types/src/provider-settings/index.ts @@ -0,0 +1,78 @@ +import { anthropicProviderDefinition } from "./anthropic.js" +import { openRouterProviderDefinition } from "./openrouter.js" +import { bedrockProviderDefinition } from "./bedrock.js" +import { vertexProviderDefinition } from "./vertex.js" +import { openAiProviderDefinition } from "./openai.js" +import { ollamaProviderDefinition } from "./ollama.js" +import { vsCodeLmProviderDefinition } from "./vscode-lm.js" +import { lmStudioProviderDefinition } from "./lm-studio.js" +import { geminiProviderDefinition } from "./gemini.js" +import { geminiCliProviderDefinition } from "./gemini-cli.js" +import { openAiCodexProviderDefinition } from "./openai-codex.js" +import { openAiNativeProviderDefinition } from "./openai-native.js" +import { mistralProviderDefinition } from "./mistral.js" +import { deepSeekProviderDefinition } from "./deepseek.js" +import { poeProviderDefinition } from "./poe.js" +import { moonshotProviderDefinition } from "./moonshot.js" +import { kimiCodeProviderDefinition } from "./kimi-code.js" +import { minimaxProviderDefinition } from "./minimax.js" +import { mimoProviderDefinition } from "./mimo.js" +import { requestyProviderDefinition } from "./requesty.js" +import { unboundProviderDefinition } from "./unbound.js" +import { fakeAiProviderDefinition } from "./fake-ai.js" +import { xaiProviderDefinition } from "./xai.js" +import { litellmProviderDefinition } from "./litellm.js" +import { sambaNovaProviderDefinition } from "./sambanova.js" +import { zaiProviderDefinition } from "./zai.js" +import { fireworksProviderDefinition } from "./fireworks.js" +import { friendliProviderDefinition } from "./friendli.js" +import { qwenCodeProviderDefinition } from "./qwen-code.js" +import { vercelAiGatewayProviderDefinition } from "./vercel-ai-gateway.js" +import { opencodeGoProviderDefinition } from "./opencode-go.js" +import { kenariProviderDefinition } from "./kenari.js" +import { zooGatewayProviderDefinition } from "./zoo-gateway.js" +import { basetenProviderDefinition } from "./baseten.js" + +import type { ProviderDefinition } from "./common.js" + +export { OPEN_AI_CODEX_SERVICE_TIER_KEY } from "./common.js" +export { kimiCodeAuthMethodSchema, type KimiCodeAuthMethod } from "./kimi-code.js" +export { zaiApiLineSchema, type ZaiApiLine } from "./zai.js" +export type { ProviderDefinition } from "./common.js" + +export const providerDefinitionList = [ + anthropicProviderDefinition, + openRouterProviderDefinition, + bedrockProviderDefinition, + vertexProviderDefinition, + openAiProviderDefinition, + ollamaProviderDefinition, + vsCodeLmProviderDefinition, + lmStudioProviderDefinition, + geminiProviderDefinition, + geminiCliProviderDefinition, + openAiCodexProviderDefinition, + openAiNativeProviderDefinition, + mistralProviderDefinition, + deepSeekProviderDefinition, + poeProviderDefinition, + moonshotProviderDefinition, + kimiCodeProviderDefinition, + minimaxProviderDefinition, + mimoProviderDefinition, + requestyProviderDefinition, + unboundProviderDefinition, + fakeAiProviderDefinition, + xaiProviderDefinition, + litellmProviderDefinition, + sambaNovaProviderDefinition, + zaiProviderDefinition, + fireworksProviderDefinition, + friendliProviderDefinition, + qwenCodeProviderDefinition, + vercelAiGatewayProviderDefinition, + opencodeGoProviderDefinition, + kenariProviderDefinition, + zooGatewayProviderDefinition, + basetenProviderDefinition, +] as const satisfies readonly ProviderDefinition[] diff --git a/packages/types/src/provider-settings/kenari.ts b/packages/types/src/provider-settings/kenari.ts new file mode 100644 index 0000000000..da1a082059 --- /dev/null +++ b/packages/types/src/provider-settings/kenari.ts @@ -0,0 +1,17 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +const KENARI_MODEL_ID_FIELD = "kenariModelId" + +export const kenariProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.kenari, + modelIdKey: KENARI_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(KENARI_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + kenariApiKey: z.string().optional(), + [KENARI_MODEL_ID_FIELD]: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/kimi-code.ts b/packages/types/src/provider-settings/kimi-code.ts new file mode 100644 index 0000000000..44e6fd8d7b --- /dev/null +++ b/packages/types/src/provider-settings/kimi-code.ts @@ -0,0 +1,23 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const kimiCodeAuthMethodSchema = z.enum(["oauth", "api-key"]) +export type KimiCodeAuthMethod = z.infer + +export const kimiCodeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.kimiCode, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + kimiCodeAuthMethod: kimiCodeAuthMethodSchema.optional(), + kimiCodeApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/litellm.ts b/packages/types/src/provider-settings/litellm.ts new file mode 100644 index 0000000000..c78b11565a --- /dev/null +++ b/packages/types/src/provider-settings/litellm.ts @@ -0,0 +1,22 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + LITELLM_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const litellmProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.litellm, + modelIdKey: LITELLM_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(LITELLM_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + litellmBaseUrl: z.string().optional(), + litellmApiKey: z.string().optional(), + [LITELLM_MODEL_ID_FIELD]: z.string().optional(), + litellmUsePromptCache: z.boolean().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/lm-studio.ts b/packages/types/src/provider-settings/lm-studio.ts new file mode 100644 index 0000000000..fefc1c0c8f --- /dev/null +++ b/packages/types/src/provider-settings/lm-studio.ts @@ -0,0 +1,22 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + LM_STUDIO_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const lmStudioProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.lmstudio, + modelIdKey: LM_STUDIO_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(LM_STUDIO_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + [LM_STUDIO_MODEL_ID_FIELD]: z.string().optional(), + lmStudioBaseUrl: z.string().optional(), + lmStudioDraftModelId: z.string().optional(), + lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/mimo.ts b/packages/types/src/provider-settings/mimo.ts new file mode 100644 index 0000000000..690ede3839 --- /dev/null +++ b/packages/types/src/provider-settings/mimo.ts @@ -0,0 +1,27 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const mimoProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.mimo, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + mimoBaseUrl: z + .union([ + z.literal("https://api.xiaomimimo.com/v1"), + z.literal("https://token-plan-cn.xiaomimimo.com/v1"), + z.literal("https://token-plan-sgp.xiaomimimo.com/v1"), + z.literal("https://token-plan-ams.xiaomimimo.com/v1"), + ]) + .optional(), + mimoApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/minimax.ts b/packages/types/src/provider-settings/minimax.ts new file mode 100644 index 0000000000..86e8b1ce98 --- /dev/null +++ b/packages/types/src/provider-settings/minimax.ts @@ -0,0 +1,22 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const minimaxProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.minimax, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + minimaxBaseUrl: z + .union([z.literal("https://api.minimax.io/v1"), z.literal("https://api.minimaxi.com/v1")]) + .optional(), + minimaxApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/mistral.ts b/packages/types/src/provider-settings/mistral.ts new file mode 100644 index 0000000000..d2353563b6 --- /dev/null +++ b/packages/types/src/provider-settings/mistral.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const mistralProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.mistral, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + mistralApiKey: z.string().optional(), + mistralCodestralUrl: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/moonshot.ts b/packages/types/src/provider-settings/moonshot.ts new file mode 100644 index 0000000000..96292872f3 --- /dev/null +++ b/packages/types/src/provider-settings/moonshot.ts @@ -0,0 +1,25 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const moonshotProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.moonshot, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + moonshotBaseUrl: z + .union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")]) + .optional(), + moonshotApiKey: z.string().optional(), + }, +}) + +export const kimiCodeAuthMethodSchema = z.enum(["oauth", "api-key"]) +export type KimiCodeAuthMethod = z.infer diff --git a/packages/types/src/provider-settings/ollama.ts b/packages/types/src/provider-settings/ollama.ts new file mode 100644 index 0000000000..d736fd20b5 --- /dev/null +++ b/packages/types/src/provider-settings/ollama.ts @@ -0,0 +1,22 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + OLLAMA_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const ollamaProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.ollama, + modelIdKey: OLLAMA_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OLLAMA_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + [OLLAMA_MODEL_ID_FIELD]: z.string().optional(), + ollamaBaseUrl: z.string().optional(), + ollamaApiKey: z.string().optional(), + ollamaNumCtx: z.number().int().min(128).optional(), + }, +}) diff --git a/packages/types/src/provider-settings/openai-codex.ts b/packages/types/src/provider-settings/openai-codex.ts new file mode 100644 index 0000000000..6703290e1e --- /dev/null +++ b/packages/types/src/provider-settings/openai-codex.ts @@ -0,0 +1,20 @@ +import { providerIdentifiers } from "../provider-identifiers.js" +import { openAiCodexServiceTierSchema } from "../model.js" +import { + API_MODEL_ID_FIELD, + OPEN_AI_CODEX_SERVICE_TIER_KEY, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const openAiCodexProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openaiCodex, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + // Codex "Fast" mode maps to the Responses API priority service tier. + [OPEN_AI_CODEX_SERVICE_TIER_KEY]: openAiCodexServiceTierSchema.optional(), + }, +}) diff --git a/packages/types/src/provider-settings/openai-native.ts b/packages/types/src/provider-settings/openai-native.ts new file mode 100644 index 0000000000..683fd4dfee --- /dev/null +++ b/packages/types/src/provider-settings/openai-native.ts @@ -0,0 +1,24 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { serviceTierSchema } from "../model.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const openAiNativeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openaiNative, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + openAiNativeApiKey: z.string().optional(), + openAiNativeBaseUrl: z.string().optional(), + // OpenAI Responses API service tier for openai-native provider only. + // UI should only expose this when the selected model supports flex/priority. + openAiNativeServiceTier: serviceTierSchema.optional(), + }, +}) diff --git a/packages/types/src/provider-settings/openai.ts b/packages/types/src/provider-settings/openai.ts new file mode 100644 index 0000000000..7ef5e3dc76 --- /dev/null +++ b/packages/types/src/provider-settings/openai.ts @@ -0,0 +1,29 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { modelInfoSchema } from "../model.js" +import { + OPEN_AI_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const openAiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openai, + modelIdKey: OPEN_AI_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OPEN_AI_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + openAiBaseUrl: z.string().optional(), + openAiApiKey: z.string().optional(), + openAiR1FormatEnabled: z.boolean().optional(), + [OPEN_AI_MODEL_ID_FIELD]: z.string().optional(), + openAiCustomModelInfo: modelInfoSchema.nullish(), + openAiUseAzure: z.boolean().optional(), + azureApiVersion: z.string().optional(), + openAiStreamingEnabled: z.boolean().optional(), + openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. + openAiHeaders: z.record(z.string(), z.string()).optional(), + }, +}) diff --git a/packages/types/src/provider-settings/opencode-go.ts b/packages/types/src/provider-settings/opencode-go.ts new file mode 100644 index 0000000000..6bfb2e50cc --- /dev/null +++ b/packages/types/src/provider-settings/opencode-go.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + OPENCODE_GO_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const opencodeGoProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.opencodeGo, + modelIdKey: OPENCODE_GO_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OPENCODE_GO_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + opencodeGoApiKey: z.string().optional(), + [OPENCODE_GO_MODEL_ID_FIELD]: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/openrouter.ts b/packages/types/src/provider-settings/openrouter.ts new file mode 100644 index 0000000000..84cc42375d --- /dev/null +++ b/packages/types/src/provider-settings/openrouter.ts @@ -0,0 +1,22 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + OPEN_ROUTER_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const openRouterProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.openrouter, + modelIdKey: OPEN_ROUTER_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(OPEN_ROUTER_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + openRouterApiKey: z.string().optional(), + [OPEN_ROUTER_MODEL_ID_FIELD]: z.string().optional(), + openRouterBaseUrl: z.string().optional(), + openRouterSpecificProvider: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/poe.ts b/packages/types/src/provider-settings/poe.ts new file mode 100644 index 0000000000..d5c1055490 --- /dev/null +++ b/packages/types/src/provider-settings/poe.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const poeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.poe, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + poeApiKey: z.string().optional(), + poeBaseUrl: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/qwen-code.ts b/packages/types/src/provider-settings/qwen-code.ts new file mode 100644 index 0000000000..c1c2f8be81 --- /dev/null +++ b/packages/types/src/provider-settings/qwen-code.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const qwenCodeProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.qwenCode, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + qwenCodeOauthPath: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/requesty.ts b/packages/types/src/provider-settings/requesty.ts new file mode 100644 index 0000000000..ae3b5205d6 --- /dev/null +++ b/packages/types/src/provider-settings/requesty.ts @@ -0,0 +1,21 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + REQUESTY_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const requestyProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.requesty, + modelIdKey: REQUESTY_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(REQUESTY_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + requestyBaseUrl: z.string().optional(), + requestyApiKey: z.string().optional(), + [REQUESTY_MODEL_ID_FIELD]: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/sambanova.ts b/packages/types/src/provider-settings/sambanova.ts new file mode 100644 index 0000000000..257883747b --- /dev/null +++ b/packages/types/src/provider-settings/sambanova.ts @@ -0,0 +1,23 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const sambaNovaProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.sambanova, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + sambaNovaApiKey: z.string().optional(), + }, +}) + +export const zaiApiLineSchema = z.enum(["international_coding", "china_coding", "international_api", "china_api"]) + +export type ZaiApiLine = z.infer diff --git a/packages/types/src/provider-settings/unbound.ts b/packages/types/src/provider-settings/unbound.ts new file mode 100644 index 0000000000..bfd4dd02c4 --- /dev/null +++ b/packages/types/src/provider-settings/unbound.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + UNBOUND_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const unboundProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.unbound, + modelIdKey: UNBOUND_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(UNBOUND_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + unboundApiKey: z.string().optional(), + [UNBOUND_MODEL_ID_FIELD]: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/vercel-ai-gateway.ts b/packages/types/src/provider-settings/vercel-ai-gateway.ts new file mode 100644 index 0000000000..fb892423ce --- /dev/null +++ b/packages/types/src/provider-settings/vercel-ai-gateway.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + VERCEL_AI_GATEWAY_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const vercelAiGatewayProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.vercelAiGateway, + modelIdKey: VERCEL_AI_GATEWAY_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(VERCEL_AI_GATEWAY_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + vercelAiGatewayApiKey: z.string().optional(), + [VERCEL_AI_GATEWAY_MODEL_ID_FIELD]: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/vertex.ts b/packages/types/src/provider-settings/vertex.ts new file mode 100644 index 0000000000..2bfa3b114e --- /dev/null +++ b/packages/types/src/provider-settings/vertex.ts @@ -0,0 +1,23 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const vertexProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.vertex, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + vertexKeyFile: z.string().optional(), + vertexJsonCredentials: z.string().optional(), + vertexProjectId: z.string().optional(), + vertexRegion: z.string().optional(), + vertex1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + }, +}) diff --git a/packages/types/src/provider-settings/vscode-lm.ts b/packages/types/src/provider-settings/vscode-lm.ts new file mode 100644 index 0000000000..8579d69a5b --- /dev/null +++ b/packages/types/src/provider-settings/vscode-lm.ts @@ -0,0 +1,20 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { baseProviderSettingsShape, createProviderDefinition } from "./common.js" + +export const vsCodeLmProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.vscodeLm, + getModelId: (settings) => settings.vsCodeLmModelSelector?.id, + schema: { + ...baseProviderSettingsShape, + vsCodeLmModelSelector: z + .object({ + vendor: z.string().optional(), + family: z.string().optional(), + version: z.string().optional(), + id: z.string().optional(), + }) + .optional(), + }, +}) diff --git a/packages/types/src/provider-settings/xai.ts b/packages/types/src/provider-settings/xai.ts new file mode 100644 index 0000000000..d5c2fbb874 --- /dev/null +++ b/packages/types/src/provider-settings/xai.ts @@ -0,0 +1,19 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const xaiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.xai, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + xaiApiKey: z.string().optional(), + }, +}) diff --git a/packages/types/src/provider-settings/zai.ts b/packages/types/src/provider-settings/zai.ts new file mode 100644 index 0000000000..0f4c4a0333 --- /dev/null +++ b/packages/types/src/provider-settings/zai.ts @@ -0,0 +1,23 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + API_MODEL_ID_FIELD, + apiModelIdProviderModelShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const zaiApiLineSchema = z.enum(["international_coding", "china_coding", "international_api", "china_api"]) +export type ZaiApiLine = z.infer + +export const zaiProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.zai, + modelIdKey: API_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(API_MODEL_ID_FIELD), + schema: { + ...apiModelIdProviderModelShape, + zaiApiKey: z.string().optional(), + zaiApiLine: zaiApiLineSchema.optional(), + }, +}) diff --git a/packages/types/src/provider-settings/zoo-gateway.ts b/packages/types/src/provider-settings/zoo-gateway.ts new file mode 100644 index 0000000000..020bd0509d --- /dev/null +++ b/packages/types/src/provider-settings/zoo-gateway.ts @@ -0,0 +1,21 @@ +import { z } from "zod" + +import { providerIdentifiers } from "../provider-identifiers.js" +import { + ZOO_GATEWAY_MODEL_ID_FIELD, + baseProviderSettingsShape, + createModelIdAccessor, + createProviderDefinition, +} from "./common.js" + +export const zooGatewayProviderDefinition = createProviderDefinition({ + apiProvider: providerIdentifiers.zooGateway, + modelIdKey: ZOO_GATEWAY_MODEL_ID_FIELD, + getModelId: createModelIdAccessor(ZOO_GATEWAY_MODEL_ID_FIELD), + schema: { + ...baseProviderSettingsShape, + zooSessionToken: z.string().optional(), + [ZOO_GATEWAY_MODEL_ID_FIELD]: z.string().optional(), + zooGatewayBaseUrl: z.string().optional(), + }, +}) From 936eb632326677ba3c3ae7d5cadb2f89d9959ad9 Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 17:24:50 +0300 Subject: [PATCH 14/16] refactor(types): colocate provider setting keys --- packages/types/src/provider-settings/common.ts | 11 ----------- packages/types/src/provider-settings/index.ts | 2 +- packages/types/src/provider-settings/litellm.ts | 9 +++------ packages/types/src/provider-settings/lm-studio.ts | 9 +++------ packages/types/src/provider-settings/ollama.ts | 9 +++------ packages/types/src/provider-settings/openai-codex.ts | 3 ++- packages/types/src/provider-settings/openai.ts | 9 +++------ packages/types/src/provider-settings/opencode-go.ts | 9 +++------ packages/types/src/provider-settings/openrouter.ts | 9 +++------ packages/types/src/provider-settings/requesty.ts | 9 +++------ packages/types/src/provider-settings/unbound.ts | 9 +++------ .../types/src/provider-settings/vercel-ai-gateway.ts | 9 +++------ packages/types/src/provider-settings/zoo-gateway.ts | 9 +++------ 13 files changed, 33 insertions(+), 73 deletions(-) diff --git a/packages/types/src/provider-settings/common.ts b/packages/types/src/provider-settings/common.ts index 18e595e7a8..e73a05f143 100644 --- a/packages/types/src/provider-settings/common.ts +++ b/packages/types/src/provider-settings/common.ts @@ -6,17 +6,6 @@ import type { ProviderIdentifier } from "../provider-identifiers.js" export const API_PROVIDER_FIELD = "apiProvider" export const SETTINGS_SHAPE_FIELD = "settingsShape" export const API_MODEL_ID_FIELD = "apiModelId" -export const OPEN_ROUTER_MODEL_ID_FIELD = "openRouterModelId" -export const OPEN_AI_MODEL_ID_FIELD = "openAiModelId" -export const OLLAMA_MODEL_ID_FIELD = "ollamaModelId" -export const LM_STUDIO_MODEL_ID_FIELD = "lmStudioModelId" -export const REQUESTY_MODEL_ID_FIELD = "requestyModelId" -export const UNBOUND_MODEL_ID_FIELD = "unboundModelId" -export const LITELLM_MODEL_ID_FIELD = "litellmModelId" -export const VERCEL_AI_GATEWAY_MODEL_ID_FIELD = "vercelAiGatewayModelId" -export const OPENCODE_GO_MODEL_ID_FIELD = "opencodeGoModelId" -export const ZOO_GATEWAY_MODEL_ID_FIELD = "zooGatewayModelId" -export const OPEN_AI_CODEX_SERVICE_TIER_KEY = "openAiCodexServiceTier" export const baseProviderSettingsShape = { includeMaxTokens: z.boolean().optional(), diff --git a/packages/types/src/provider-settings/index.ts b/packages/types/src/provider-settings/index.ts index ce291ac155..e97fbf5995 100644 --- a/packages/types/src/provider-settings/index.ts +++ b/packages/types/src/provider-settings/index.ts @@ -35,7 +35,7 @@ import { basetenProviderDefinition } from "./baseten.js" import type { ProviderDefinition } from "./common.js" -export { OPEN_AI_CODEX_SERVICE_TIER_KEY } from "./common.js" +export { OPEN_AI_CODEX_SERVICE_TIER_KEY } from "./openai-codex.js" export { kimiCodeAuthMethodSchema, type KimiCodeAuthMethod } from "./kimi-code.js" export { zaiApiLineSchema, type ZaiApiLine } from "./zai.js" export type { ProviderDefinition } from "./common.js" diff --git a/packages/types/src/provider-settings/litellm.ts b/packages/types/src/provider-settings/litellm.ts index c78b11565a..018cbdecdd 100644 --- a/packages/types/src/provider-settings/litellm.ts +++ b/packages/types/src/provider-settings/litellm.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - LITELLM_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const LITELLM_MODEL_ID_FIELD = "litellmModelId" export const litellmProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.litellm, diff --git a/packages/types/src/provider-settings/lm-studio.ts b/packages/types/src/provider-settings/lm-studio.ts index fefc1c0c8f..dea9659d9b 100644 --- a/packages/types/src/provider-settings/lm-studio.ts +++ b/packages/types/src/provider-settings/lm-studio.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - LM_STUDIO_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const LM_STUDIO_MODEL_ID_FIELD = "lmStudioModelId" export const lmStudioProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.lmstudio, diff --git a/packages/types/src/provider-settings/ollama.ts b/packages/types/src/provider-settings/ollama.ts index d736fd20b5..323484ad8d 100644 --- a/packages/types/src/provider-settings/ollama.ts +++ b/packages/types/src/provider-settings/ollama.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - OLLAMA_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const OLLAMA_MODEL_ID_FIELD = "ollamaModelId" export const ollamaProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.ollama, diff --git a/packages/types/src/provider-settings/openai-codex.ts b/packages/types/src/provider-settings/openai-codex.ts index 6703290e1e..93117224ae 100644 --- a/packages/types/src/provider-settings/openai-codex.ts +++ b/packages/types/src/provider-settings/openai-codex.ts @@ -2,12 +2,13 @@ import { providerIdentifiers } from "../provider-identifiers.js" import { openAiCodexServiceTierSchema } from "../model.js" import { API_MODEL_ID_FIELD, - OPEN_AI_CODEX_SERVICE_TIER_KEY, apiModelIdProviderModelShape, createModelIdAccessor, createProviderDefinition, } from "./common.js" +export const OPEN_AI_CODEX_SERVICE_TIER_KEY = "openAiCodexServiceTier" + export const openAiCodexProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openaiCodex, modelIdKey: API_MODEL_ID_FIELD, diff --git a/packages/types/src/provider-settings/openai.ts b/packages/types/src/provider-settings/openai.ts index 7ef5e3dc76..651d90f60d 100644 --- a/packages/types/src/provider-settings/openai.ts +++ b/packages/types/src/provider-settings/openai.ts @@ -2,12 +2,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" import { modelInfoSchema } from "../model.js" -import { - OPEN_AI_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const OPEN_AI_MODEL_ID_FIELD = "openAiModelId" export const openAiProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openai, diff --git a/packages/types/src/provider-settings/opencode-go.ts b/packages/types/src/provider-settings/opencode-go.ts index 6bfb2e50cc..f1e51890a0 100644 --- a/packages/types/src/provider-settings/opencode-go.ts +++ b/packages/types/src/provider-settings/opencode-go.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - OPENCODE_GO_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const OPENCODE_GO_MODEL_ID_FIELD = "opencodeGoModelId" export const opencodeGoProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.opencodeGo, diff --git a/packages/types/src/provider-settings/openrouter.ts b/packages/types/src/provider-settings/openrouter.ts index 84cc42375d..8ed4480f88 100644 --- a/packages/types/src/provider-settings/openrouter.ts +++ b/packages/types/src/provider-settings/openrouter.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - OPEN_ROUTER_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const OPEN_ROUTER_MODEL_ID_FIELD = "openRouterModelId" export const openRouterProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.openrouter, diff --git a/packages/types/src/provider-settings/requesty.ts b/packages/types/src/provider-settings/requesty.ts index ae3b5205d6..ec37d6bb68 100644 --- a/packages/types/src/provider-settings/requesty.ts +++ b/packages/types/src/provider-settings/requesty.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - REQUESTY_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const REQUESTY_MODEL_ID_FIELD = "requestyModelId" export const requestyProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.requesty, diff --git a/packages/types/src/provider-settings/unbound.ts b/packages/types/src/provider-settings/unbound.ts index bfd4dd02c4..cadecf5912 100644 --- a/packages/types/src/provider-settings/unbound.ts +++ b/packages/types/src/provider-settings/unbound.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - UNBOUND_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const UNBOUND_MODEL_ID_FIELD = "unboundModelId" export const unboundProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.unbound, diff --git a/packages/types/src/provider-settings/vercel-ai-gateway.ts b/packages/types/src/provider-settings/vercel-ai-gateway.ts index fb892423ce..c9d838d7bc 100644 --- a/packages/types/src/provider-settings/vercel-ai-gateway.ts +++ b/packages/types/src/provider-settings/vercel-ai-gateway.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - VERCEL_AI_GATEWAY_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const VERCEL_AI_GATEWAY_MODEL_ID_FIELD = "vercelAiGatewayModelId" export const vercelAiGatewayProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.vercelAiGateway, diff --git a/packages/types/src/provider-settings/zoo-gateway.ts b/packages/types/src/provider-settings/zoo-gateway.ts index 020bd0509d..d2082bff33 100644 --- a/packages/types/src/provider-settings/zoo-gateway.ts +++ b/packages/types/src/provider-settings/zoo-gateway.ts @@ -1,12 +1,9 @@ import { z } from "zod" import { providerIdentifiers } from "../provider-identifiers.js" -import { - ZOO_GATEWAY_MODEL_ID_FIELD, - baseProviderSettingsShape, - createModelIdAccessor, - createProviderDefinition, -} from "./common.js" +import { baseProviderSettingsShape, createModelIdAccessor, createProviderDefinition } from "./common.js" + +export const ZOO_GATEWAY_MODEL_ID_FIELD = "zooGatewayModelId" export const zooGatewayProviderDefinition = createProviderDefinition({ apiProvider: providerIdentifiers.zooGateway, From 3b4937db65fc1ef89453d0f4ca2330d4e90c3b8c Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 17:29:46 +0300 Subject: [PATCH 15/16] test(webview): use canonical provider identifiers --- .../src/utils/__tests__/validate.spec.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/webview-ui/src/utils/__tests__/validate.spec.ts b/webview-ui/src/utils/__tests__/validate.spec.ts index 26f2041fd3..8e2e223977 100644 --- a/webview-ui/src/utils/__tests__/validate.spec.ts +++ b/webview-ui/src/utils/__tests__/validate.spec.ts @@ -1,4 +1,9 @@ -import { type ProviderSettings, type OrganizationAllowList, type RouterModels } from "@roo-code/types" +import { + providerIdentifiers, + type ProviderSettings, + type OrganizationAllowList, + type RouterModels, +} from "@roo-code/types" // Mock i18next to return translation keys with interpolated values vi.mock("i18next", () => ({ @@ -117,16 +122,22 @@ describe("Model Validation Functions", () => { it.each([ { name: "OpenAI Native", - config: { apiProvider: "openai-native", apiModelId: "blocked-model" } satisfies ProviderSettings, + config: { + apiProvider: providerIdentifiers.openaiNative, + apiModelId: "blocked-model", + } satisfies ProviderSettings, }, { name: "OpenAI Compatible", - config: { apiProvider: "openai", openAiModelId: "blocked-model" } satisfies ProviderSettings, + config: { + apiProvider: providerIdentifiers.openai, + openAiModelId: "blocked-model", + } satisfies ProviderSettings, }, { name: "VS Code LM", config: { - apiProvider: "vscode-lm", + apiProvider: providerIdentifiers.vscodeLm, vsCodeLmModelSelector: { id: "blocked-model" }, } satisfies ProviderSettings, }, From 45eb5779e445a737a0bc192722bcba46d3a0d7ac Mon Sep 17 00:00:00 2001 From: gubin-dev Date: Tue, 11 Aug 2026 17:43:19 +0300 Subject: [PATCH 16/16] docs(types): explain model provider exclusions --- packages/types/src/provider-settings.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 547abca6dc..240e34e152 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -393,6 +393,7 @@ export const getApiProtocol = (provider: ProviderName | undefined, modelId?: str export const MODELS_BY_PROVIDER: Record< Exclude< ProviderName, + // OpenAI is custom-configured; Fake AI and Gemini CLI do not expose model lists. typeof providerIdentifiers.fakeAi | typeof providerIdentifiers.geminiCli | typeof providerIdentifiers.openai >, { id: ProviderName; label: string; models: string[] }