feat: Implement Azure dual SDK routing for Claude and GPT models, add new Azure model definitions, and introduce Azure API settings UI with dedicated tests.

This commit is contained in:
iskandarsulaili 2025-11-21 07:01:08 +08:00
parent 35467fd5ef
commit cade653333
9 changed files with 423 additions and 39 deletions

View file

@ -1,7 +1,6 @@
import type { RooCodeAPI } from "@roo-code/types"
declare global {
// eslint-disable-next-line no-var
var api: RooCodeAPI
}

View file

@ -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)
})
})
})
})

View file

@ -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")
})
})
})

View file

@ -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"
}

View file

@ -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<string, ModelInfo>

80
pnpm-lock.yaml generated
View file

@ -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

View file

@ -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};

View file

@ -279,6 +279,15 @@
"openRouterTransformsText": "Compress prompts and message chains to the context size (<a>OpenRouter Transforms</a>)",
"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://<your-azure>.services.ai.azure.com/anthropic/ | For GPT: https://<your-azure>.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",

View file

@ -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://<your-azure>.services.ai.azure.com/anthropic/ | GPT: https://<your-azure>.cognitiveservices.azure.com/",
"azureShowAdvanced": "显示高级设置",
"azureDeploymentName": "部署名称(可选)",
"azureDeploymentNameHint": "覆盖默认部署名称。留空则使用模型 ID。",
"azureApiVersion": "API 版本(可选)",
"azureApiVersionHint": "可选的 API 版本覆盖。默认值Claude=2023-06-01GPT=2024-12-01-preview",
"cerebrasApiKey": "Cerebras API 密钥",
"getCerebrasApiKey": "获取 Cerebras API 密钥",
"chutesApiKey": "Chutes API 密钥",