From 52bbda183db2a0ae74014c66423905b307bbce75 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sun, 21 Jun 2026 21:10:20 -0700 Subject: [PATCH] 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 --- apps/vscode/proto/cline/models.proto | 6 +++++ .../model-catalog/effective-config.test.ts | 25 +++++++++++++++++++ .../src/sdk/model-catalog/provider-id.test.ts | 8 ++++++ .../src/sdk/model-catalog/provider-id.ts | 4 +++ .../src/sdk/model-catalog/store.test.ts | 19 ++++++++++++++ apps/vscode/src/shared/api.ts | 4 +++ .../api-configuration-conversion.test.ts | 21 ++++++++++++++++ .../models/api-configuration-conversion.ts | 16 ++++++++++++ .../src/shared/providers/providers.json | 16 ++++++++++++ apps/vscode/vitest.config.ts | 1 + .../providerSettingsRegistry.test.ts | 9 +++++++ .../providers/providerSettingsRegistry.ts | 8 ++++++ 12 files changed, 137 insertions(+) create mode 100644 apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.test.ts diff --git a/apps/vscode/proto/cline/models.proto b/apps/vscode/proto/cline/models.proto index c8ceb44b0a..e69fe88794 100644 --- a/apps/vscode/proto/cline/models.proto +++ b/apps/vscode/proto/cline/models.proto @@ -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 { diff --git a/apps/vscode/src/sdk/model-catalog/effective-config.test.ts b/apps/vscode/src/sdk/model-catalog/effective-config.test.ts index 4db4de4d58..cdb0f2bbf8 100644 --- a/apps/vscode/src/sdk/model-catalog/effective-config.test.ts +++ b/apps/vscode/src/sdk/model-catalog/effective-config.test.ts @@ -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({ diff --git a/apps/vscode/src/sdk/model-catalog/provider-id.test.ts b/apps/vscode/src/sdk/model-catalog/provider-id.test.ts index 15c3ff4f94..d1b39f50ad 100644 --- a/apps/vscode/src/sdk/model-catalog/provider-id.test.ts +++ b/apps/vscode/src/sdk/model-catalog/provider-id.test.ts @@ -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", () => { diff --git a/apps/vscode/src/sdk/model-catalog/provider-id.ts b/apps/vscode/src/sdk/model-catalog/provider-id.ts index 4df164d80f..8fce5c1f59 100644 --- a/apps/vscode/src/sdk/model-catalog/provider-id.ts +++ b/apps/vscode/src/sdk/model-catalog/provider-id.ts @@ -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 diff --git a/apps/vscode/src/sdk/model-catalog/store.test.ts b/apps/vscode/src/sdk/model-catalog/store.test.ts index 8809d96625..c05d17bd42 100644 --- a/apps/vscode/src/sdk/model-catalog/store.test.ts +++ b/apps/vscode/src/sdk/model-catalog/store.test.ts @@ -31,6 +31,9 @@ const mocks = vi.hoisted(() => { getSavedProviderSettings(providerId: string): Record | 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() diff --git a/apps/vscode/src/shared/api.ts b/apps/vscode/src/shared/api.ts index 28fc9bd241..94ce6ae493 100644 --- a/apps/vscode/src/shared/api.ts +++ b/apps/vscode/src/shared/api.ts @@ -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 diff --git a/apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.test.ts b/apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.test.ts new file mode 100644 index 0000000000..f9091d17f2 --- /dev/null +++ b/apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.test.ts @@ -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, + }) + } + }) +}) diff --git a/apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.ts b/apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.ts index 8437ddd343..dd6ff6537f 100644 --- a/apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.ts +++ b/apps/vscode/src/shared/proto-conversions/models/api-configuration-conversion.ts @@ -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" } diff --git a/apps/vscode/src/shared/providers/providers.json b/apps/vscode/src/shared/providers/providers.json index 99e52812d9..9172a3c07c 100644 --- a/apps/vscode/src/shared/providers/providers.json +++ b/apps/vscode/src/shared/providers/providers.json @@ -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" } ] } diff --git a/apps/vscode/vitest.config.ts b/apps/vscode/vitest.config.ts index 610726b310..1dc9d91869 100644 --- a/apps/vscode/vitest.config.ts +++ b/apps/vscode/vitest.config.ts @@ -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", diff --git a/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.test.ts b/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.test.ts index 6de069074c..e423739297 100644 --- a/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.test.ts +++ b/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.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() }) }) diff --git a/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.ts b/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.ts index 5c2e4d2f7d..df10e148bd 100644 --- a/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.ts +++ b/apps/vscode/webview-ui/src/components/settings/providers/providerSettingsRegistry.ts @@ -50,6 +50,7 @@ const GENERIC_PROVIDER_PRESENTATION_OVERRIDES: Record