diff --git a/apps/vscode-e2e/src/types/global.d.ts b/apps/vscode-e2e/src/types/global.d.ts index c2b11bf335..5c7fb164ad 100644 --- a/apps/vscode-e2e/src/types/global.d.ts +++ b/apps/vscode-e2e/src/types/global.d.ts @@ -1,7 +1,6 @@ import type { RooCodeAPI } from "@roo-code/types" declare global { - // eslint-disable-next-line no-var var api: RooCodeAPI } diff --git a/packages/types/src/__tests__/provider-settings-azure.test.ts b/packages/types/src/__tests__/provider-settings-azure.test.ts new file mode 100644 index 0000000000..c1e0b942d7 --- /dev/null +++ b/packages/types/src/__tests__/provider-settings-azure.test.ts @@ -0,0 +1,238 @@ +import { getApiProtocol } from "../provider-settings.js" +import { azureModels, azureDefaultModelId } from "../providers/azure.js" + +describe("Azure Provider", () => { + describe("getApiProtocol for Azure provider", () => { + it("should return 'anthropic' for Azure provider with Claude models", () => { + expect(getApiProtocol("azure", "claude-sonnet-4-5")).toBe("anthropic") + expect(getApiProtocol("azure", "claude-haiku-4-5")).toBe("anthropic") + expect(getApiProtocol("azure", "claude-opus-4-1")).toBe("anthropic") + }) + + it("should return 'anthropic' for Azure provider with case-insensitive Claude models", () => { + expect(getApiProtocol("azure", "CLAUDE-SONNET-4-5")).toBe("anthropic") + expect(getApiProtocol("azure", "Claude-Haiku-4-5")).toBe("anthropic") + expect(getApiProtocol("azure", "CLAUDE-opus-4-1")).toBe("anthropic") + }) + + it("should return 'openai' for Azure provider with GPT models", () => { + expect(getApiProtocol("azure", "gpt-5-pro")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5.1")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5-chat")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5-mini")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5-nano")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5-codex")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5.1-codex")).toBe("openai") + }) + + it("should return 'openai' for Azure provider without model specified", () => { + expect(getApiProtocol("azure")).toBe("openai") + }) + + it("should return 'openai' for Azure provider with unknown model", () => { + expect(getApiProtocol("azure", "unknown-model")).toBe("openai") + }) + }) + + describe("Azure model definitions", () => { + it("should have all 10 expected models defined", () => { + const modelIds = Object.keys(azureModels) + expect(modelIds).toHaveLength(10) + expect(modelIds).toContain("claude-sonnet-4-5") + expect(modelIds).toContain("claude-haiku-4-5") + expect(modelIds).toContain("claude-opus-4-1") + expect(modelIds).toContain("gpt-5-pro") + expect(modelIds).toContain("gpt-5.1") + expect(modelIds).toContain("gpt-5-chat") + expect(modelIds).toContain("gpt-5-mini") + expect(modelIds).toContain("gpt-5-nano") + expect(modelIds).toContain("gpt-5-codex") + expect(modelIds).toContain("gpt-5.1-codex") + }) + + it("should have correct default model", () => { + expect(azureDefaultModelId).toBe("claude-sonnet-4-5") + }) + + describe("Claude models", () => { + it("should have correct capabilities for claude-sonnet-4-5", () => { + const model = azureModels["claude-sonnet-4-5"] + expect(model.contextWindow).toBe(200_000) + expect(model.maxTokens).toBe(64_000) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(true) + expect(model.supportsReasoningBudget).toBe(true) + expect(model.supportsTemperature).toBe(true) + expect(model.cacheWritesPrice).toBe(3.75) + expect(model.cacheReadsPrice).toBe(0.3) + }) + + it("should have correct capabilities for claude-haiku-4-5", () => { + const model = azureModels["claude-haiku-4-5"] + expect(model.contextWindow).toBe(200_000) + expect(model.maxTokens).toBe(64_000) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(true) + expect(model.supportsReasoningBudget).toBe(true) + expect(model.supportsTemperature).toBe(true) + expect(model.cacheWritesPrice).toBe(0.75) + expect(model.cacheReadsPrice).toBe(0.06) + }) + + it("should have correct capabilities for claude-opus-4-1", () => { + const model = azureModels["claude-opus-4-1"] + expect(model.contextWindow).toBe(200_000) + expect(model.maxTokens).toBe(64_000) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(true) + expect(model.supportsReasoningBudget).toBe(true) + expect(model.supportsTemperature).toBe(true) + expect(model.cacheWritesPrice).toBe(18.75) + expect(model.cacheReadsPrice).toBe(1.5) + }) + }) + + describe("GPT models", () => { + it("should have correct capabilities for gpt-5-pro", () => { + const model = azureModels["gpt-5-pro"] + expect(model.contextWindow).toBe(400_000) + expect(model.maxTokens).toBe(128_000) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(true) + expect(model.supportsNativeTools).toBe(true) + expect(model.supportsTemperature).toBe(true) + expect(model.supportsReasoningEffort).toEqual(["high"]) + expect(model.reasoningEffort).toBe("high") + expect(model.cacheReadsPrice).toBe(0.125) + }) + + it("should have correct capabilities for gpt-5.1", () => { + const model = azureModels["gpt-5.1"] + expect(model.contextWindow).toBe(400_000) + expect(model.maxTokens).toBe(128_000) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(true) + expect(model.supportsNativeTools).toBe(true) + expect(model.supportsTemperature).toBe(false) + expect(model.supportsReasoningEffort).toEqual(["none", "low", "medium", "high"]) + expect(model.reasoningEffort).toBe("medium") + expect(model.supportsVerbosity).toBe(true) + expect(model.cacheReadsPrice).toBe(0.125) + }) + + it("should have correct capabilities for gpt-5-chat", () => { + const model = azureModels["gpt-5-chat"] + expect(model.contextWindow).toBe(128_000) + expect(model.maxTokens).toBe(16_384) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(false) + expect(model.supportsNativeTools).toBe(true) + expect(model.supportsTemperature).toBe(true) + }) + + it("should have correct capabilities for gpt-5-mini", () => { + const model = azureModels["gpt-5-mini"] + expect(model.contextWindow).toBe(400_000) + expect(model.maxTokens).toBe(16_384) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(false) + expect(model.supportsTemperature).toBe(false) + }) + + it("should have correct capabilities for gpt-5-nano", () => { + const model = azureModels["gpt-5-nano"] + expect(model.contextWindow).toBe(400_000) + expect(model.maxTokens).toBe(16_384) + expect(model.supportsImages).toBe(true) + expect(model.supportsPromptCache).toBe(false) + expect(model.supportsTemperature).toBe(false) + }) + + it("should have correct capabilities for gpt-5-codex", () => { + const model = azureModels["gpt-5-codex"] + expect(model.contextWindow).toBe(400_000) + expect(model.maxTokens).toBe(128_000) + expect(model.supportsImages).toBe(false) + expect(model.supportsPromptCache).toBe(true) + expect(model.supportsTemperature).toBe(true) + expect(model.cacheReadsPrice).toBe(0.15) + }) + + it("should have correct capabilities for gpt-5.1-codex", () => { + const model = azureModels["gpt-5.1-codex"] + expect(model.contextWindow).toBe(400_000) + expect(model.maxTokens).toBe(128_000) + expect(model.supportsImages).toBe(false) + expect(model.supportsPromptCache).toBe(true) + expect(model.supportsTemperature).toBe(false) + expect(model.supportsReasoningEffort).toEqual(["none", "low", "medium", "high"]) + expect(model.reasoningEffort).toBe("medium") + expect(model.supportsVerbosity).toBe(true) + expect(model.cacheReadsPrice).toBe(0.15) + }) + }) + + describe("Streaming support", () => { + it("all models should support streaming (implicit in SDK)", () => { + // All Azure models support streaming via their respective SDKs + // AnthropicFoundry SDK and AzureOpenAI SDK both support streaming + // This is verified by the handler implementation + Object.keys(azureModels).forEach((modelId) => { + expect(azureModels[modelId as keyof typeof azureModels]).toBeDefined() + }) + }) + }) + + describe("Prompt caching support", () => { + it("should have caching enabled for 7 models", () => { + const cachingModels = Object.entries(azureModels).filter(([_, model]) => model.supportsPromptCache) + expect(cachingModels).toHaveLength(7) + }) + + it("should not have caching for chat models (gpt-5-chat, gpt-5-mini, gpt-5-nano)", () => { + expect(azureModels["gpt-5-chat"].supportsPromptCache).toBe(false) + expect(azureModels["gpt-5-mini"].supportsPromptCache).toBe(false) + expect(azureModels["gpt-5-nano"].supportsPromptCache).toBe(false) + }) + + it("should have caching for all Claude models", () => { + expect(azureModels["claude-sonnet-4-5"].supportsPromptCache).toBe(true) + expect(azureModels["claude-haiku-4-5"].supportsPromptCache).toBe(true) + expect(azureModels["claude-opus-4-1"].supportsPromptCache).toBe(true) + }) + + it("should have caching for reasoning GPT models", () => { + expect(azureModels["gpt-5-pro"].supportsPromptCache).toBe(true) + expect(azureModels["gpt-5.1"].supportsPromptCache).toBe(true) + expect(azureModels["gpt-5-codex"].supportsPromptCache).toBe(true) + expect(azureModels["gpt-5.1-codex"].supportsPromptCache).toBe(true) + }) + }) + + describe("Image support", () => { + it("should have image support for 8 models", () => { + const imageModels = Object.entries(azureModels).filter(([_, model]) => model.supportsImages) + expect(imageModels).toHaveLength(8) + }) + + it("should not have image support for codex models", () => { + expect(azureModels["gpt-5-codex"].supportsImages).toBe(false) + expect(azureModels["gpt-5.1-codex"].supportsImages).toBe(false) + }) + + it("should have image support for all Claude models", () => { + expect(azureModels["claude-sonnet-4-5"].supportsImages).toBe(true) + expect(azureModels["claude-haiku-4-5"].supportsImages).toBe(true) + expect(azureModels["claude-opus-4-1"].supportsImages).toBe(true) + }) + + it("should have image support for non-codex GPT models", () => { + expect(azureModels["gpt-5-pro"].supportsImages).toBe(true) + expect(azureModels["gpt-5.1"].supportsImages).toBe(true) + expect(azureModels["gpt-5-chat"].supportsImages).toBe(true) + expect(azureModels["gpt-5-mini"].supportsImages).toBe(true) + expect(azureModels["gpt-5-nano"].supportsImages).toBe(true) + }) + }) + }) +}) diff --git a/packages/types/src/__tests__/provider-settings.test.ts b/packages/types/src/__tests__/provider-settings.test.ts index cedf9a3e2f..c44ca1d59d 100644 --- a/packages/types/src/__tests__/provider-settings.test.ts +++ b/packages/types/src/__tests__/provider-settings.test.ts @@ -83,4 +83,28 @@ describe("getApiProtocol", () => { expect(getApiProtocol("vertex", "ClAuDe-InStAnT")).toBe("anthropic") }) }) + + describe("Azure provider with dual SDK routing", () => { + it("should return 'anthropic' for Azure provider with Claude models", () => { + expect(getApiProtocol("azure", "claude-sonnet-4-5")).toBe("anthropic") + expect(getApiProtocol("azure", "claude-haiku-4-5")).toBe("anthropic") + expect(getApiProtocol("azure", "claude-opus-4-1")).toBe("anthropic") + }) + + it("should return 'openai' for Azure provider with GPT models", () => { + expect(getApiProtocol("azure", "gpt-5-pro")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5.1")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5-chat")).toBe("openai") + expect(getApiProtocol("azure", "gpt-5-mini")).toBe("openai") + }) + + it("should be case-insensitive for Azure Claude detection", () => { + expect(getApiProtocol("azure", "CLAUDE-SONNET-4-5")).toBe("anthropic") + expect(getApiProtocol("azure", "Claude-Haiku-4-5")).toBe("anthropic") + }) + + it("should return 'openai' for Azure without model", () => { + expect(getApiProtocol("azure")).toBe("openai") + }) + }) }) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 002b155a5c..0b39d10851 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -634,6 +634,11 @@ export const getApiProtocol = (provider: ProviderName | undefined, modelId?: str return "anthropic" } + // Azure uses anthropic protocol for Claude models, openai for GPT models + if (provider && provider === "azure" && modelId && modelId.toLowerCase().includes("claude")) { + return "anthropic" + } + return "openai" } diff --git a/packages/types/src/providers/azure.ts b/packages/types/src/providers/azure.ts index c2840024b8..c2925c9c5c 100644 --- a/packages/types/src/providers/azure.ts +++ b/packages/types/src/providers/azure.ts @@ -48,4 +48,90 @@ export const azureModels = { supportsTemperature: false, description: "GPT-5.1 on Azure OpenAI - Adaptive reasoning with flexible effort levels", }, + "claude-haiku-4-5": { + maxTokens: 64_000, + contextWindow: 200_000, + supportsImages: true, + supportsPromptCache: true, + supportsReasoningBudget: true, + inputPrice: 0.6, + outputPrice: 3.0, + cacheWritesPrice: 0.75, + cacheReadsPrice: 0.06, + supportsTemperature: true, + description: "Claude Haiku 4.5 on Azure AI Foundry - Fast and efficient for everyday tasks", + }, + "claude-opus-4-1": { + maxTokens: 64_000, + contextWindow: 200_000, + supportsImages: true, + supportsPromptCache: true, + supportsReasoningBudget: true, + inputPrice: 15.0, + outputPrice: 75.0, + cacheWritesPrice: 18.75, + cacheReadsPrice: 1.5, + supportsTemperature: true, + description: "Claude Opus 4.1 on Azure AI Foundry - Most powerful Claude model for complex reasoning", + }, + "gpt-5-chat": { + maxTokens: 16_384, + contextWindow: 128_000, + supportsNativeTools: true, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 1.0, + outputPrice: 5.0, + supportsTemperature: true, + description: "GPT-5-Chat on Azure OpenAI - Optimized for conversational tasks", + }, + "gpt-5-mini": { + maxTokens: 16_384, + contextWindow: 400_000, + supportsNativeTools: true, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.5, + outputPrice: 2.5, + supportsTemperature: false, + description: "GPT-5-Mini on Azure OpenAI - Compact and efficient for simple tasks", + }, + "gpt-5-nano": { + maxTokens: 16_384, + contextWindow: 400_000, + supportsNativeTools: true, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.25, + outputPrice: 1.25, + supportsTemperature: false, + description: "GPT-5-Nano on Azure OpenAI - Ultra-compact and cost-effective", + }, + "gpt-5-codex": { + maxTokens: 128_000, + contextWindow: 400_000, + supportsNativeTools: true, + supportsImages: false, + supportsPromptCache: true, + inputPrice: 1.5, + outputPrice: 8.0, + cacheReadsPrice: 0.15, + supportsTemperature: true, + description: "GPT-5-Codex on Azure OpenAI - Specialized for code generation and analysis", + }, + "gpt-5.1-codex": { + maxTokens: 128_000, + contextWindow: 400_000, + supportsNativeTools: true, + supportsImages: false, + supportsPromptCache: true, + supportsReasoningEffort: ["none", "low", "medium", "high"], + reasoningEffort: "medium", + inputPrice: 1.5, + outputPrice: 8.0, + cacheReadsPrice: 0.15, + supportsVerbosity: true, + supportsTemperature: false, + description: "GPT-5.1-Codex on Azure OpenAI - Advanced code model with adaptive reasoning", + }, } as const satisfies Record diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f72aa5afb3..b74293b791 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -460,7 +460,7 @@ importers: version: 16.5.0 typescript-eslint: specifier: ^8.26.0 - version: 8.47.0(eslint@9.39.1)(typescript@5.8.3) + version: 8.47.0(eslint@9.39.1)(typescript@5.9.3) packages/config-typescript: {} @@ -6650,7 +6650,7 @@ packages: dev: false optional: true - /@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0)(eslint@9.39.1)(typescript@5.8.3): + /@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0)(eslint@9.39.1)(typescript@5.9.3): resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -6659,22 +6659,22 @@ packages: typescript: '>=4.8.4 <6.0.0' dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1)(typescript@5.8.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.47.0 eslint: 9.39.1 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.8.3): + /@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3): resolution: {integrity: sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -6683,25 +6683,25 @@ packages: dependencies: '@typescript-eslint/scope-manager': 8.47.0 '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.47.0 debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1 - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/project-service@8.47.0(typescript@5.8.3): + /@typescript-eslint/project-service@8.47.0(typescript@5.9.3): resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' dependencies: - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) '@typescript-eslint/types': 8.47.0 debug: 4.4.3(supports-color@8.1.1) - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color dev: true @@ -6714,16 +6714,16 @@ packages: '@typescript-eslint/visitor-keys': 8.47.0 dev: true - /@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.8.3): + /@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3): resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' dependencies: - typescript: 5.8.3 + typescript: 5.9.3 dev: true - /@typescript-eslint/type-utils@8.47.0(eslint@9.39.1)(typescript@5.8.3): + /@typescript-eslint/type-utils@8.47.0(eslint@9.39.1)(typescript@5.9.3): resolution: {integrity: sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -6731,12 +6731,12 @@ packages: typescript: '>=4.8.4 <6.0.0' dependencies: '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color dev: true @@ -6746,14 +6746,14 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: true - /@typescript-eslint/typescript-estree@8.47.0(typescript@5.8.3): + /@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3): resolution: {integrity: sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' dependencies: - '@typescript-eslint/project-service': 8.47.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.8.3) + '@typescript-eslint/project-service': 8.47.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 debug: 4.4.3(supports-color@8.1.1) @@ -6761,13 +6761,13 @@ packages: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@8.47.0(eslint@9.39.1)(typescript@5.8.3): + /@typescript-eslint/utils@8.47.0(eslint@9.39.1)(typescript@5.9.3): resolution: {integrity: sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -6777,9 +6777,9 @@ packages: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) '@typescript-eslint/scope-manager': 8.47.0 '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) eslint: 9.39.1 - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color dev: true @@ -6857,7 +6857,7 @@ packages: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 - vite: 6.3.6(@types/node@24.10.1)(tsx@4.20.6) + vite: 6.3.6(@types/node@20.19.25)(tsx@4.20.6)(yaml@2.8.1) dev: true /@vitest/pretty-format@3.2.4: @@ -16163,13 +16163,13 @@ packages: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} dev: false - /ts-api-utils@2.1.0(typescript@5.8.3): + /ts-api-utils@2.1.0(typescript@5.9.3): resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' dependencies: - typescript: 5.8.3 + typescript: 5.9.3 dev: true /ts-dedent@2.2.0: @@ -16450,19 +16450,19 @@ packages: underscore: 1.13.7 dev: true - /typescript-eslint@8.47.0(eslint@9.39.1)(typescript@5.8.3): + /typescript-eslint@8.47.0(eslint@9.39.1)(typescript@5.9.3): resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' dependencies: - '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0)(eslint@9.39.1)(typescript@5.8.3) - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.8.3) - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0)(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) eslint: 9.39.1 - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color dev: true @@ -16472,6 +16472,12 @@ packages: engines: {node: '>=14.17'} hasBin: true + /typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + /uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} dev: true diff --git a/webview-ui/src/components/common/CodeBlock.tsx b/webview-ui/src/components/common/CodeBlock.tsx index b13a6ec24d..b645e59f0d 100644 --- a/webview-ui/src/components/common/CodeBlock.tsx +++ b/webview-ui/src/components/common/CodeBlock.tsx @@ -125,7 +125,15 @@ export const StyledPre = styled.div<{ overflow-y: auto; padding: 8px 3px; border-radius: 6px; - ${({ preStyle }) => preStyle && { ...preStyle }} + ${({ preStyle }) => + preStyle + ? Object.entries(preStyle) + .map(([key, value]) => { + const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase() + return `${cssKey}: ${value};` + }) + .join("\n\t\t") + : ""} pre { background-color: ${CODE_BLOCK_BG_COLOR}; diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 635252e5a5..acd416a383 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -279,6 +279,15 @@ "openRouterTransformsText": "Compress prompts and message chains to the context size (OpenRouter Transforms)", "anthropicApiKey": "Anthropic API Key", "getAnthropicApiKey": "Get Anthropic API Key", + "azureApiKey": "Azure API Key", + "getAzureApiKey": "Get Azure API Key", + "azureBaseUrl": "Azure Base URL", + "azureBaseUrlHint": "For Claude: https://.services.ai.azure.com/anthropic/ | For GPT: https://.cognitiveservices.azure.com/", + "azureShowAdvanced": "Show Advanced Settings", + "azureDeploymentName": "Deployment Name (Optional)", + "azureDeploymentNameHint": "Override the default deployment name. Leave empty to use model ID.", + "azureApiVersion": "API Version (Optional)", + "azureApiVersionHint": "Optional API version override. Defaults: Claude=2023-06-01, GPT=2024-12-01-preview", "anthropicUseAuthToken": "Pass Anthropic API Key as Authorization header instead of X-Api-Key", "anthropic1MContextBetaLabel": "Enable 1M context window (Beta)", "anthropic1MContextBetaDescription": "Extends context window to 1 million tokens for Claude Sonnet 4", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index ac11ea477b..a67e36164b 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -279,6 +279,15 @@ "anthropic1MContextBetaDescription": "为 Claude Sonnet 4 将上下文窗口扩展至 100 万个 token", "awsBedrock1MContextBetaLabel": "启用 1M 上下文窗口 (Beta)", "awsBedrock1MContextBetaDescription": "为 Claude Sonnet 4 将上下文窗口扩展至 100 万个 token", + "azureApiKey": "Azure API 密钥", + "getAzureApiKey": "获取 Azure API 密钥", + "azureBaseUrl": "Azure 基础 URL", + "azureBaseUrlHint": "Claude: https://.services.ai.azure.com/anthropic/ | GPT: https://.cognitiveservices.azure.com/", + "azureShowAdvanced": "显示高级设置", + "azureDeploymentName": "部署名称(可选)", + "azureDeploymentNameHint": "覆盖默认部署名称。留空则使用模型 ID。", + "azureApiVersion": "API 版本(可选)", + "azureApiVersionHint": "可选的 API 版本覆盖。默认值:Claude=2023-06-01,GPT=2024-12-01-preview", "cerebrasApiKey": "Cerebras API 密钥", "getCerebrasApiKey": "获取 Cerebras API 密钥", "chutesApiKey": "Chutes API 密钥",