feat(vscode): expose additional SDK providers (#11703)

* feat(vscode): expose additional SDK providers

* fix(vscode): reserve skipped provider enum slots

* fix(vscode): keep Z.AI Coding Plan provider-specific
This commit is contained in:
Saoud Rizwan
2026-06-24 14:11:36 +09:00
committed by Dominic Cooney
parent 80036cef44
commit 52bbda183d
12 changed files with 137 additions and 0 deletions
+6
View File
@@ -613,6 +613,12 @@ enum ApiProvider {
OPENAI_CODEX = 40;
WANDB = 41;
CLINE_PASS = 42;
POOLSIDE = 45;
V0 = 46;
XIAOMI = 47;
ZAI_CODING_PLAN = 49;
reserved 43, 44, 48;
reserved "OPENAI_CODEX_CLI", "OPENCODE", "KILO";
}
enum ApiFormat {
@@ -117,6 +117,31 @@ describe("buildEffectiveProviderConfig", () => {
})
})
it("reads the Z.AI Coding Plan API key from provider-specific settings", async () => {
const { buildEffectiveProviderConfig } = await import("./effective-config")
mocks.setProviderSettings({
"zai-coding-plan": { provider: "zai-coding-plan", apiKey: "provider-zai-coding-plan-key" },
})
mocks.setApiConfiguration({ zaiApiKey: "state-zai-key" })
expect(buildEffectiveProviderConfig(parseProviderId("zai-coding-plan"))).toEqual({
providerId: parseProviderId("zai-coding-plan"),
apiKey: "provider-zai-coding-plan-key",
})
})
it("does not reuse the legacy Z.AI API key for Z.AI Coding Plan", async () => {
const { buildEffectiveProviderConfig } = await import("./effective-config")
mocks.setProviderSettings({
"zai-coding-plan": { provider: "zai-coding-plan" },
})
mocks.setApiConfiguration({ zaiApiKey: "state-zai-key" })
expect(buildEffectiveProviderConfig(parseProviderId("zai-coding-plan"))).toEqual({
providerId: parseProviderId("zai-coding-plan"),
})
})
it("respects remote-config-locked LiteLLM key already applied by StateManager", async () => {
const { buildEffectiveProviderConfig } = await import("./effective-config")
mocks.setProviderSettings({
@@ -45,6 +45,10 @@ describe("parseProviderId", () => {
parseProviderId("anthropic")
parseProviderId("openai")
parseProviderId("nousResearch")
parseProviderId("zai-coding-plan")
parseProviderId("poolside")
parseProviderId("v0")
parseProviderId("xiaomi")
expect(warnSpy).not.toHaveBeenCalled()
})
@@ -56,6 +60,10 @@ describe("isKnownProviderId", () => {
expect(isKnownProviderId(parseProviderId("openai"))).toBe(true)
expect(isKnownProviderId(parseProviderId("deepseek"))).toBe(true)
expect(isKnownProviderId(parseProviderId("nousResearch"))).toBe(true)
expect(isKnownProviderId(parseProviderId("zai-coding-plan"))).toBe(true)
expect(isKnownProviderId(parseProviderId("poolside"))).toBe(true)
expect(isKnownProviderId(parseProviderId("v0"))).toBe(true)
expect(isKnownProviderId(parseProviderId("xiaomi"))).toBe(true)
})
it("returns false for a custom provider id", () => {
@@ -41,18 +41,22 @@ const KNOWN_API_PROVIDERS = {
cerebras: true,
sapaicore: true,
groq: true,
poolside: true,
huggingface: true,
"huawei-cloud-maas": true,
dify: true,
baseten: true,
"vercel-ai-gateway": true,
v0: true,
zai: true,
"zai-coding-plan": true,
oca: true,
aihubmix: true,
minimax: true,
hicap: true,
nousResearch: true,
wandb: true,
xiaomi: true,
"cline-pass": true,
} satisfies Record<ApiProvider, true>
@@ -31,6 +31,9 @@ const mocks = vi.hoisted(() => {
getSavedProviderSettings(providerId: string): Record<string, unknown> | undefined {
return providerSettingsById[providerId]
},
getApiConfiguration(): MockApiConfiguration {
return { ...apiConfiguration }
},
getSaveProviderSettingsMock(): typeof saveProviderSettings {
return saveProviderSettings
},
@@ -185,6 +188,22 @@ describe("createProviderConfigStore", () => {
})
})
it("writes Z.AI Coding Plan API keys only to provider-specific settings", async () => {
const { createProviderConfigStore } = await import("./store")
mocks.setApiConfiguration({ zaiApiKey: "shared-zai-key" })
const store = createProviderConfigStore()
const providerId = parseProviderId("zai-coding-plan")
const written = store.write(providerId, { apiKey: "coding-plan-key" })
expect(written).toEqual({ providerId, apiKey: "coding-plan-key" })
expect(mocks.getSavedProviderSettings("zai-coding-plan")).toMatchObject({
provider: "zai-coding-plan",
apiKey: "coding-plan-key",
})
expect(mocks.getApiConfiguration().zaiApiKey).toBe("shared-zai-key")
})
it("returns undefined from readSelection when modelId or modelInfo is missing", async () => {
const { createProviderConfigStore } = await import("./store")
const store = createProviderConfigStore()
+4
View File
@@ -33,18 +33,22 @@ export type ApiProvider =
| "cerebras"
| "sapaicore"
| "groq"
| "poolside"
| "huggingface"
| "huawei-cloud-maas"
| "dify"
| "baseten"
| "vercel-ai-gateway"
| "v0"
| "zai"
| "zai-coding-plan"
| "oca"
| "aihubmix"
| "minimax"
| "hicap"
| "nousResearch"
| "wandb"
| "xiaomi"
export const DEFAULT_API_PROVIDER = "openrouter" as ApiProvider
@@ -0,0 +1,21 @@
import type { ApiProvider } from "@shared/api"
import { describe, expect, it } from "vitest"
import { convertApiConfigurationToProto, convertProtoToApiConfiguration } from "./api-configuration-conversion"
describe("api configuration provider conversion", () => {
it("round-trips SDK provider ids added after the legacy enum list", () => {
const providers: ApiProvider[] = ["poolside", "v0", "xiaomi", "zai-coding-plan"]
for (const provider of providers) {
const proto = convertApiConfigurationToProto({
actModeApiProvider: provider,
planModeApiProvider: provider,
})
expect(convertProtoToApiConfiguration(proto)).toMatchObject({
actModeApiProvider: provider,
planModeApiProvider: provider,
})
}
})
})
@@ -330,6 +330,14 @@ function convertApiProviderToProto(provider: string | undefined): ProtoApiProvid
return ProtoApiProvider.NOUSRESEARCH
case "openai-codex":
return ProtoApiProvider.OPENAI_CODEX
case "poolside":
return ProtoApiProvider.POOLSIDE
case "v0":
return ProtoApiProvider.V0
case "xiaomi":
return ProtoApiProvider.XIAOMI
case "zai-coding-plan":
return ProtoApiProvider.ZAI_CODING_PLAN
default:
return ProtoApiProvider.ANTHROPIC
}
@@ -424,6 +432,14 @@ export function convertProtoToApiProvider(provider: ProtoApiProvider): ApiProvid
return "nousResearch"
case ProtoApiProvider.OPENAI_CODEX:
return "openai-codex"
case ProtoApiProvider.POOLSIDE:
return "poolside"
case ProtoApiProvider.V0:
return "v0"
case ProtoApiProvider.XIAOMI:
return "xiaomi"
case ProtoApiProvider.ZAI_CODING_PLAN:
return "zai-coding-plan"
default:
return "anthropic"
}
@@ -12,6 +12,10 @@
"value": "openai-codex",
"label": "ChatGPT Subscription"
},
{
"value": "zai-coding-plan",
"label": "Z.AI Coding Plan"
},
{
"value": "gemini",
"label": "Google Gemini"
@@ -76,6 +80,10 @@
"value": "groq",
"label": "Groq"
},
{
"value": "poolside",
"label": "Poolside"
},
{
"value": "cerebras",
"label": "Cerebras"
@@ -84,6 +92,10 @@
"value": "vercel-ai-gateway",
"label": "Vercel AI Gateway"
},
{
"value": "v0",
"label": "Vercel v0"
},
{
"value": "baseten",
"label": "Baseten"
@@ -171,6 +183,10 @@
{
"value": "wandb",
"label": "W&B Inference by CoreWeave"
},
{
"value": "xiaomi",
"label": "Xiaomi"
}
]
}
+1
View File
@@ -9,6 +9,7 @@ export default defineConfig({
include: [
"src/sdk/**/*.test.ts",
"src/shared/vsCodeSelectorUtils.test.ts",
"src/shared/proto-conversions/models/**/*.test.ts",
"src/core/storage/remote-config/**/*.test.ts",
"src/services/mcp/__tests__/settingsLock.test.ts",
"src/shared/model-catalog/provider-helpers.test.ts",
@@ -67,9 +67,13 @@ describe("providerSettingsRegistry", () => {
["mistral", "Mistral", "https://console.mistral.ai/codestral"],
["nebius", "Nebius", "https://auth.tokenfactory.nebius.com/ui/login"],
["nousResearch", "NousResearch", undefined],
["poolside", "Poolside", undefined],
["sambanova", "SambaNova", "https://docs.sambanova.ai/cloud/docs/get-started/overview"],
["vercel-ai-gateway", "Vercel AI Gateway", "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai"],
["v0", "Vercel v0", undefined],
["wandb", "W&B", "https://wandb.ai"],
["xiaomi", "Xiaomi", undefined],
["zai-coding-plan", "Z.AI Coding Plan", undefined],
] as const
for (const [providerId, providerName, signupUrl] of migratedProviders) {
@@ -171,6 +175,11 @@ describe("providerSettingsRegistry", () => {
providerName: "Together",
signupUrl: "https://api.together.ai/settings/api-keys",
})
expect(getFallbackGenericProviderSettings("zai-coding-plan")).toEqual({
allowsCustomIds: false,
providerId: "zai-coding-plan",
providerName: "Z.AI Coding Plan",
})
expect(getFallbackGenericProviderSettings("openai")).toBeUndefined()
})
})
@@ -50,6 +50,7 @@ const GENERIC_PROVIDER_PRESENTATION_OVERRIDES: Record<string, GenericProviderPre
groq: {
signupUrl: "https://console.groq.com/keys",
},
poolside: {},
cerebras: {
signupUrl: "https://cloud.cerebras.ai/",
},
@@ -90,9 +91,12 @@ const GENERIC_PROVIDER_PRESENTATION_OVERRIDES: Record<string, GenericProviderPre
"vercel-ai-gateway": {
signupUrl: "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai",
},
v0: {},
wandb: {
signupUrl: "https://wandb.ai",
},
xiaomi: {},
"zai-coding-plan": {},
}
const GENERIC_PROVIDER_PROTOCOLS = new Set(["anthropic", "gemini", "openai-chat", "openai-responses"])
@@ -137,8 +141,12 @@ const FALLBACK_GENERIC_PROVIDER_NAMES = {
minimax: "MiniMax",
mistral: "Mistral",
nousResearch: "NousResearch",
poolside: "Poolside",
together: "Together",
v0: "Vercel v0",
wandb: "W&B",
xiaomi: "Xiaomi",
"zai-coding-plan": "Z.AI Coding Plan",
} as const
export function getFallbackGenericProviderSettings(providerId: string): GenericProviderSettingsConfig | undefined {