diff --git a/.changeset/openai-compatible-settings-improvements.md b/.changeset/openai-compatible-settings-improvements.md new file mode 100644 index 00000000000..c2d051eb2fd --- /dev/null +++ b/.changeset/openai-compatible-settings-improvements.md @@ -0,0 +1,10 @@ +--- +"kilo-code": minor +--- + +Add reasoning and capability controls for OpenAI Compatible models + +- Added checkboxes for 'Supports Reasoning', 'Supports Function Calling', and 'Supports Computer Use' to the OpenAI Compatible settings UI. +- Compacted the capability checkboxes into a 2-column grid layout with tooltip-only descriptions. +- Updated OpenAiHandler to inject the 'thinking' parameter when reasoning is enabled and the model supports it. +- Gated tool inclusion based on the 'supportsNativeTools' flag. diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 72d6b9a7f32..bbd38590fb2 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -186,6 +186,7 @@ jobs: libxkbfile-dev \ pkg-config \ build-essential \ + libkrb5-dev \ python3 - name: Turbo cache setup uses: actions/cache@v5 diff --git a/.github/workflows/marketplace-publish.yml b/.github/workflows/marketplace-publish.yml index cc0c17bb6a8..1b9805953d4 100644 --- a/.github/workflows/marketplace-publish.yml +++ b/.github/workflows/marketplace-publish.yml @@ -201,6 +201,7 @@ jobs: libxkbfile-dev \ pkg-config \ build-essential \ + libkrb5-dev \ python3 - name: Turbo cache setup uses: actions/cache@v5 diff --git a/apps/kilocode-docs/docs/features/api-configuration-profiles.md b/apps/kilocode-docs/docs/features/api-configuration-profiles.md index ec9349d6330..b7cd47ed066 100644 --- a/apps/kilocode-docs/docs/features/api-configuration-profiles.md +++ b/apps/kilocode-docs/docs/features/api-configuration-profiles.md @@ -38,15 +38,15 @@ Note that available settings vary by provider and model. Each provider offers di Provider selection dropdown - Enter API key - API key entry field + API key entry field - Choose a model - Model selection interface + Model selection interface - Adjust model parameters - Model parameter adjustment controls + Model parameter adjustment controls ### Switching Profiles diff --git a/apps/kilocode-docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/api-configuration-profiles.md b/apps/kilocode-docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/api-configuration-profiles.md index 705b7eb5085..350a2ec1d3c 100644 --- a/apps/kilocode-docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/api-configuration-profiles.md +++ b/apps/kilocode-docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/features/api-configuration-profiles.md @@ -36,19 +36,19 @@ API 配置配置文件允许您创建和切换不同的 AI 设置集。每个配 - 选择您的 API 提供商 - 提供商选择下拉菜单 + 提供商选择下拉菜单 - 输入 API 密钥 - API 密钥输入字段 + API 密钥输入字段 - 选择模型 - 模型选择界面 + 模型选择界面 - 调整模型参数 - 模型参数调整控件 + 模型参数调整控件 ### 切换配置文件 diff --git a/cli/src/auth/index.ts b/cli/src/auth/index.ts index ccad78f42fb..58725075211 100644 --- a/cli/src/auth/index.ts +++ b/cli/src/auth/index.ts @@ -27,13 +27,13 @@ export default async function authWizard(): Promise { // Prompt user to select a provider const selectedProvider = await withRawMode(() => - select({ - message: "Select an AI provider:", - choices: providerChoices, - loop: false, - pageSize: process.stdout.rows ? Math.min(20, process.stdout.rows - 2) : 10, - }) - ) + select({ + message: "Select an AI provider:", + choices: providerChoices, + loop: false, + pageSize: process.stdout.rows ? Math.min(20, process.stdout.rows - 2) : 10, + }), + ) // Find the selected provider const provider = authProviders.find((p) => p.value === selectedProvider) @@ -86,14 +86,14 @@ export default async function authWizard(): Promise { }) const selectedModel = await withRawMode(() => - select({ - message: "Select a model to use:", - choices: modelChoices, - default: defaultModel, - loop: false, - pageSize: 10, - }) - ) + select({ + message: "Select a model to use:", + choices: modelChoices, + default: defaultModel, + loop: false, + pageSize: 10, + }), + ) const modelKey = getModelIdKey(providerId) authResult.providerConfig[modelKey] = selectedModel diff --git a/cli/src/commands/checkpoint.ts b/cli/src/commands/checkpoint.ts index 7edaad02aec..f519776c4c2 100644 --- a/cli/src/commands/checkpoint.ts +++ b/cli/src/commands/checkpoint.ts @@ -222,8 +222,8 @@ function getCheckpointsPath(): string { } /** - * Handle /checkpoint disable - */ + * Handle /checkpoint disable + */ async function handleDisable(context: CommandContext): Promise { const { addMessage, sendWebviewMessage } = context diff --git a/cli/src/services/__tests__/autocomplete.detectInputState.test.ts b/cli/src/services/__tests__/autocomplete.detectInputState.test.ts index 137fe8cd25c..62e3a6957fb 100644 --- a/cli/src/services/__tests__/autocomplete.detectInputState.test.ts +++ b/cli/src/services/__tests__/autocomplete.detectInputState.test.ts @@ -67,8 +67,18 @@ describe("detectInputState", () => { { condition: (context) => context.getArgument("subcommand") === "select", provider: async () => [ - { value: "personal", description: "Personal account", matchScore: 1, highlightedValue: "personal" }, - { value: "kilo-code", description: "Kilo Code team", matchScore: 1, highlightedValue: "kilo-code" }, + { + value: "personal", + description: "Personal account", + matchScore: 1, + highlightedValue: "personal", + }, + { + value: "kilo-code", + description: "Kilo Code team", + matchScore: 1, + highlightedValue: "kilo-code", + }, ], }, ], @@ -108,7 +118,12 @@ describe("detectInputState", () => { }, provider: async () => [ { value: "gpt-4", description: "GPT-4", matchScore: 1, highlightedValue: "gpt-4" }, - { value: "claude-sonnet", description: "Claude Sonnet", matchScore: 1, highlightedValue: "claude-sonnet" }, + { + value: "claude-sonnet", + description: "Claude Sonnet", + matchScore: 1, + highlightedValue: "claude-sonnet", + }, ], }, ], @@ -132,7 +147,12 @@ describe("detectInputState", () => { required: true, provider: async () => [ { value: "code", description: "Code mode", matchScore: 1, highlightedValue: "code" }, - { value: "architect", description: "Architect mode", matchScore: 1, highlightedValue: "architect" }, + { + value: "architect", + description: "Architect mode", + matchScore: 1, + highlightedValue: "architect", + }, ], }, ], diff --git a/cli/src/state/hooks/__tests__/useSessionCost.test.ts b/cli/src/state/hooks/__tests__/useSessionCost.test.ts index 74e34923243..e141e9207ab 100644 --- a/cli/src/state/hooks/__tests__/useSessionCost.test.ts +++ b/cli/src/state/hooks/__tests__/useSessionCost.test.ts @@ -125,7 +125,6 @@ describe("useSessionCost", () => { expect(result.hasCostData).toBe(true) }) - it("should handle messages with empty text", () => { const messages: ExtensionChatMessage[] = [ { diff --git a/docs/context-window-autofill.md b/docs/context-window-autofill.md new file mode 100644 index 00000000000..f9fa0d257c5 --- /dev/null +++ b/docs/context-window-autofill.md @@ -0,0 +1,45 @@ +# Context Window Auto-fill Feature + +## Objective + +Implement an auto-fill feature for the context window and other model capabilities in the OpenAI Compatible settings. + +## Changes + +### Backend + +1. **`src/shared/WebviewMessage.ts`**: + + - Added `requestOpenAiModelInfo` to `WebviewMessage` type. + - This message allows the frontend to request model information based on the selected model ID. + +2. **`src/shared/ExtensionMessage.ts`**: + + - Added `openAiModelInfo` to `ExtensionMessage` type. + - This property carriers the `ModelInfo` payload back to the frontend. + +3. **`src/api/providers/openai.ts`**: + + - Imported known model maps (`openAiNativeModels`, `anthropicModels`, etc.) from `@roo-code/types`. + - Added `getOpenAiModelInfo(modelId: string)` helper function. + - This function iterates through known model maps to find and return the `ModelInfo` for a given model ID. + +4. **`src/core/webview/webviewMessageHandler.ts`**: + - Added a handler for `requestOpenAiModelInfo`. + - It calls `getOpenAiModelInfo` and sends back an `openAiModelInfo` message with the result. + +### Frontend + +1. **`webview-ui/src/i18n/locales/en/settings.json`**: + + - Added `"autoFill": "Auto-fill"` translation key. + +2. **`webview-ui/src/components/settings/providers/OpenAICompatible.tsx`**: + - Imported `vscode` utility for message passing. + - Implemented `handleAutoFill` function that sends `requestOpenAiModelInfo`. + - Added a listener in `onMessage` to handle `openAiModelInfo` response and update `openAiCustomModelInfo` state. + - Added an "Auto-fill" button in the "Model Capabilities" section header. + +## Verification + +- Ran `pnpm check-types` successfully, confirming type safety across the monorepo. diff --git a/jetbrains/scripts/check-dependencies.js b/jetbrains/scripts/check-dependencies.js index 244998e427e..8ad70fd4aa4 100755 --- a/jetbrains/scripts/check-dependencies.js +++ b/jetbrains/scripts/check-dependencies.js @@ -309,6 +309,21 @@ function checkBuildSystem() { const gradlew = path.join(pluginDir, process.platform === "win32" ? "gradlew.bat" : "gradlew") const buildGradle = path.join(pluginDir, "build.gradle.kts") const gradleProps = path.join(pluginDir, "gradle.properties") + const gradlePropsTemplate = path.join(pluginDir, "gradle.properties.template") + + // Auto-generate gradle.properties from template if missing + if (!fs.existsSync(gradleProps) && fs.existsSync(gradlePropsTemplate)) { + try { + printWarning("gradle.properties is missing, generating from template...") + let content = fs.readFileSync(gradlePropsTemplate, "utf8") + // Use a default version for CI check - strict version sync happens later via sync:version + content = content.replace("{{VERSION}}", "0.0.0-dev") + fs.writeFileSync(gradleProps, content) + printFix("Generated gradle.properties from template") + } catch (error) { + printError(`Failed to generate gradle.properties: ${error.message}`) + } + } if (fs.existsSync(gradlew) && fs.existsSync(buildGradle) && fs.existsSync(gradleProps)) { printSuccess("Gradle build system is configured") diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index adb0c5a15be..333abaa3665 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -9,6 +9,14 @@ import { NATIVE_TOOL_DEFAULTS, DEEP_SEEK_DEFAULT_TEMPERATURE, OPENAI_AZURE_AI_INFERENCE_PATH, + openAiNativeModels, + anthropicModels, + geminiModels, + mistralModels, + deepSeekModels, + qwenCodeModels, + vertexModels, + bedrockModels, } from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" @@ -165,11 +173,17 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl stream: true as const, ...(isGrokXAI ? {} : { stream_options: { include_usage: true } }), ...(reasoning && reasoning), - ...(metadata?.tools && { tools: this.convertToolsForOpenAI(metadata.tools) }), - ...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }), - ...(metadata?.toolProtocol === "native" && { - parallel_tool_calls: metadata.parallelToolCalls ?? false, - }), + ...((this.options.enableReasoningEffort && modelInfo.supportsReasoningBinary + ? { thinking: { type: "enabled" } } + : {}) as any), + ...(metadata?.tools && + modelInfo.supportsNativeTools !== false && { tools: this.convertToolsForOpenAI(metadata.tools) }), + ...(metadata?.tool_choice && + modelInfo.supportsNativeTools !== false && { tool_choice: metadata.tool_choice }), + ...(metadata?.toolProtocol === "native" && + modelInfo.supportsNativeTools !== false && { + parallel_tool_calls: metadata.parallelToolCalls ?? false, + }), } // Add max_tokens if needed @@ -244,11 +258,17 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl : enabledLegacyFormat ? [systemMessage, ...convertToSimpleMessages(messages)] : [systemMessage, ...convertToOpenAiMessages(messages)], - ...(metadata?.tools && { tools: this.convertToolsForOpenAI(metadata.tools) }), - ...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }), - ...(metadata?.toolProtocol === "native" && { - parallel_tool_calls: metadata.parallelToolCalls ?? false, - }), + ...(metadata?.tools && + modelInfo.supportsNativeTools !== false && { tools: this.convertToolsForOpenAI(metadata.tools) }), + ...(metadata?.tool_choice && + modelInfo.supportsNativeTools !== false && { tool_choice: metadata.tool_choice }), + ...(metadata?.toolProtocol === "native" && + modelInfo.supportsNativeTools !== false && { + parallel_tool_calls: metadata.parallelToolCalls ?? false, + }), + ...((this.options.enableReasoningEffort && modelInfo.supportsReasoningBinary + ? { thinking: { type: "enabled" } } + : {}) as any), } // Add max_tokens if needed @@ -386,11 +406,17 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl ...(isGrokXAI ? {} : { stream_options: { include_usage: true } }), reasoning_effort: modelInfo.reasoningEffort as "low" | "medium" | "high" | undefined, temperature: undefined, - ...(metadata?.tools && { tools: this.convertToolsForOpenAI(metadata.tools) }), - ...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }), - ...(metadata?.toolProtocol === "native" && { - parallel_tool_calls: metadata.parallelToolCalls ?? false, - }), + ...(metadata?.tools && + modelInfo.supportsNativeTools !== false && { tools: this.convertToolsForOpenAI(metadata.tools) }), + ...(metadata?.tool_choice && + modelInfo.supportsNativeTools !== false && { tool_choice: metadata.tool_choice }), + ...(metadata?.toolProtocol === "native" && + modelInfo.supportsNativeTools !== false && { + parallel_tool_calls: metadata.parallelToolCalls ?? false, + }), + ...((this.options.enableReasoningEffort && modelInfo.supportsReasoningBinary + ? { thinking: { type: "enabled" } } + : {}) as any), } // O3 family models do not support the deprecated max_tokens parameter @@ -421,11 +447,17 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl ], reasoning_effort: modelInfo.reasoningEffort as "low" | "medium" | "high" | undefined, temperature: undefined, - ...(metadata?.tools && { tools: this.convertToolsForOpenAI(metadata.tools) }), - ...(metadata?.tool_choice && { tool_choice: metadata.tool_choice }), - ...(metadata?.toolProtocol === "native" && { - parallel_tool_calls: metadata.parallelToolCalls ?? false, - }), + ...(metadata?.tools && + modelInfo.supportsNativeTools !== false && { tools: this.convertToolsForOpenAI(metadata.tools) }), + ...(metadata?.tool_choice && + modelInfo.supportsNativeTools !== false && { tool_choice: metadata.tool_choice }), + ...(metadata?.toolProtocol === "native" && + modelInfo.supportsNativeTools !== false && { + parallel_tool_calls: metadata.parallelToolCalls ?? false, + }), + ...((this.options.enableReasoningEffort && modelInfo.supportsReasoningBinary + ? { thinking: { type: "enabled" } } + : {}) as any), } // O3 family models do not support the deprecated max_tokens parameter @@ -605,3 +637,60 @@ export async function getOpenAiModels(baseUrl?: string, apiKey?: string, openAiH return [] } } + +export function getOpenAiModelInfo(modelId: string): ModelInfo | undefined { + const models: Record[] = [ + openAiNativeModels, + anthropicModels, + geminiModels, + mistralModels, + deepSeekModels, + qwenCodeModels, + vertexModels, + bedrockModels, + ] + + // Helper function to sanitize and return model info + const sanitizeModelInfo = (info: ModelInfo): ModelInfo => { + if (info.tiers) { + return { + ...info, + tiers: info.tiers.map((tier) => ({ + ...tier, + // Replace Infinity/null with Number.MAX_SAFE_INTEGER (essentially unlimited) + contextWindow: + tier.contextWindow === Infinity || tier.contextWindow === null + ? Number.MAX_SAFE_INTEGER + : tier.contextWindow, + })), + } + } + return info + } + + // Try exact match first + for (const modelMap of models) { + if (modelId in modelMap) { + return sanitizeModelInfo(modelMap[modelId]) + } + } + + // Normalize search ID: remove provider prefix (e.g., "google/", "anthropic/") and convert to lowercase + const normalizedSearchId = modelId.replace(/^[a-z-]+\//i, "").toLowerCase() + + // Try fuzzy matching: find models where the key contains the normalized search ID + // or where the normalized search ID contains the model key's base name + for (const modelMap of models) { + const keys = Object.keys(modelMap) + for (const key of keys) { + const normalizedKey = key.toLowerCase() + // Check if key contains search term or search term contains key's base (without date suffix) + const keyBase = normalizedKey.replace(/-\d{8}$/, "") // Remove date suffixes like -20241022 + if (normalizedKey.includes(normalizedSearchId) || normalizedSearchId.includes(keyBase)) { + return sanitizeModelInfo(modelMap[key]) + } + } + } + + return undefined +} diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index 16d516675b8..f4f88e707c6 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -2713,6 +2713,7 @@ describe("ClineProvider - Router Models", () => { // Mock getState to return API configuration vi.spyOn(provider, "getState").mockResolvedValue({ apiConfiguration: { + apiProvider: "gemini", // kilocode_change: required for gemini to be fetched openRouterApiKey: "openrouter-key", requestyApiKey: "requesty-key", unboundApiKey: "unbound-key", @@ -2798,7 +2799,7 @@ describe("ClineProvider - Router Models", () => { litellm: mockModels, kilocode: mockModels, "nano-gpt": mockModels, // kilocode_change - ollama: mockModels, // kilocode_change + ollama: {}, // kilocode_change: ollama not fetched when apiProvider is gemini lmstudio: {}, "vercel-ai-gateway": mockModels, ovhcloud: mockModels, // kilocode_change @@ -2815,6 +2816,7 @@ describe("ClineProvider - Router Models", () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0] + // Note: This test intentionally does NOT set apiProvider to test when gemini/ollama are not fetched vi.spyOn(provider, "getState").mockResolvedValue({ apiConfiguration: { openRouterApiKey: "openrouter-key", @@ -2825,9 +2827,7 @@ describe("ClineProvider - Router Models", () => { litellmBaseUrl: "http://localhost:4000", // kilocode_change start chutesApiKey: "chutes-key", - geminiApiKey: "gemini-key", - googleGeminiBaseUrl: "https://gemini.example.com", - nanoGptApiKey: "nano-gpt-key", // kilocode_change + nanoGptApiKey: "nano-gpt-key", ovhCloudAiEndpointsApiKey: "ovhcloud-key", inceptionLabsApiKey: "inception-key", inceptionLabsBaseUrl: "https://api.inceptionlabs.ai/v1/", @@ -2842,14 +2842,13 @@ describe("ClineProvider - Router Models", () => { const { getModels } = await import("../../../api/providers/fetchers/modelCache") // Mock some providers to succeed and others to fail + // Note: gemini and ollama are NOT fetched because apiProvider is not set vi.mocked(getModels) .mockResolvedValueOnce(mockModels) // openrouter success - .mockResolvedValueOnce(mockModels) // kilocode_change: gemini success .mockRejectedValueOnce(new Error("Requesty API error")) // .mockResolvedValueOnce(mockModels) // kilocode_change glama success .mockRejectedValueOnce(new Error("Unbound API error")) // unbound fail .mockRejectedValueOnce(new Error("Kilocode-OpenRouter API error")) // kilocode-openrouter fail - .mockRejectedValueOnce(new Error("Ollama API error")) // kilocode_change .mockResolvedValueOnce(mockModels) // vercel-ai-gateway success .mockResolvedValueOnce(mockModels) // deepinfra success .mockResolvedValueOnce(mockModels) // nano-gpt success // kilocode_change @@ -2868,7 +2867,7 @@ describe("ClineProvider - Router Models", () => { routerModels: { deepinfra: mockModels, openrouter: mockModels, - gemini: mockModels, // kilocode_change + gemini: {}, // kilocode_change: not fetched when apiProvider is not "gemini" requesty: {}, glama: mockModels, // kilocode_change unbound: {}, @@ -3024,7 +3023,7 @@ describe("ClineProvider - Router Models", () => { routerModels: { deepinfra: mockModels, openrouter: mockModels, - gemini: mockModels, // kilocode_change + gemini: {}, // kilocode_change: gemini not fetched when apiProvider is not "gemini" requesty: mockModels, glama: mockModels, // kilocode_change unbound: mockModels, @@ -3033,7 +3032,7 @@ describe("ClineProvider - Router Models", () => { litellm: {}, kilocode: mockModels, "nano-gpt": mockModels, // kilocode_change - ollama: mockModels, // kilocode_change + ollama: {}, // kilocode_change: ollama not fetched when apiProvider is not "ollama" lmstudio: {}, "vercel-ai-gateway": mockModels, ovhcloud: mockModels, // kilocode_change diff --git a/src/core/webview/__tests__/webviewMessageHandler.spec.ts b/src/core/webview/__tests__/webviewMessageHandler.spec.ts index 007f39d7d98..3e49cb6d415 100644 --- a/src/core/webview/__tests__/webviewMessageHandler.spec.ts +++ b/src/core/webview/__tests__/webviewMessageHandler.spec.ts @@ -207,6 +207,7 @@ describe("webviewMessageHandler - requestRouterModels", () => { vi.clearAllMocks() mockClineProvider.getState = vi.fn().mockResolvedValue({ apiConfiguration: { + apiProvider: "gemini", // kilocode_change: required for gemini to be fetched openRouterApiKey: "openrouter-key", requestyApiKey: "requesty-key", glamaApiKey: "glama-key", // kilocode_change @@ -303,7 +304,7 @@ describe("webviewMessageHandler - requestRouterModels", () => { "nano-gpt": mockModels, // kilocode_change roo: mockModels, chutes: mockModels, - ollama: mockModels, // kilocode_change + ollama: {}, // kilocode_change: ollama not fetched when apiProvider is gemini lmstudio: {}, "vercel-ai-gateway": mockModels, huggingface: {}, @@ -399,7 +400,7 @@ describe("webviewMessageHandler - requestRouterModels", () => { routerModels: { deepinfra: mockModels, openrouter: mockModels, - gemini: mockModels, // kilocode_change + gemini: {}, // kilocode_change: gemini not fetched when apiProvider is not "gemini" requesty: mockModels, glama: mockModels, // kilocode_change synthetic: mockModels, // kilocode_change @@ -409,7 +410,7 @@ describe("webviewMessageHandler - requestRouterModels", () => { litellm: {}, kilocode: mockModels, "nano-gpt": mockModels, // kilocode_change - ollama: mockModels, // kilocode_change + ollama: {}, // kilocode_change: ollama not fetched when apiProvider is not "ollama" lmstudio: {}, "vercel-ai-gateway": mockModels, huggingface: {}, @@ -423,6 +424,25 @@ describe("webviewMessageHandler - requestRouterModels", () => { }) it("handles individual provider failures gracefully", async () => { + // Override beforeEach mock - this test requires apiProvider to NOT be gemini or ollama + mockClineProvider.getState = vi.fn().mockResolvedValue({ + apiConfiguration: { + openRouterApiKey: "openrouter-key", + requestyApiKey: "requesty-key", + glamaApiKey: "glama-key", // kilocode_change + unboundApiKey: "unbound-key", + litellmApiKey: "litellm-key", + litellmBaseUrl: "http://localhost:4000", + // kilocode_change start + chutesApiKey: "chutes-key", + nanoGptApiKey: "nano-gpt-key", + ovhCloudAiEndpointsApiKey: "ovhcloud-key", + inceptionLabsApiKey: "inception-key", + inceptionLabsBaseUrl: "https://api.inceptionlabs.ai/v1/", + // kilocode_change end + }, + }) + const mockModels: ModelRecord = { "model-1": { maxTokens: 4096, @@ -433,20 +453,23 @@ describe("webviewMessageHandler - requestRouterModels", () => { } // Mock some providers to succeed and others to fail + // Order matches candidates array in webviewMessageHandler.ts: + // 1. openrouter, 2. requesty, 3. glama, 4. unbound, 5. kilocode, 6. vercel-ai-gateway, + // 7. deepinfra, 8. nano-gpt, 9. ovhcloud, 10. inception, 11. synthetic, 12. roo, 13. chutes + // Then conditional: litellm (included because litellmApiKey and litellmBaseUrl are in config) + // Note: gemini and ollama are NOT included because apiProvider is not set to those values mockGetModels .mockResolvedValueOnce(mockModels) // openrouter - .mockResolvedValueOnce(mockModels) // kilocode_change: gemini .mockRejectedValueOnce(new Error("Requesty API error")) // requesty - .mockResolvedValueOnce(mockModels) // kilocode_change: glama + .mockResolvedValueOnce(mockModels) // glama .mockRejectedValueOnce(new Error("Unbound API error")) // unbound - .mockResolvedValueOnce(mockModels) // kilocode-openrouter - .mockRejectedValueOnce(new Error("Ollama API error")) // kilocode_change + .mockResolvedValueOnce(mockModels) // kilocode .mockResolvedValueOnce(mockModels) // vercel-ai-gateway .mockResolvedValueOnce(mockModels) // deepinfra - .mockResolvedValueOnce(mockModels) // nano-gpt // kilocode_change - .mockResolvedValueOnce(mockModels) // kilocode_change ovhcloud - .mockRejectedValueOnce(new Error("Inception API error")) // kilocode_change - .mockRejectedValueOnce(new Error("Synthetic API error")) // kilocode_change + .mockResolvedValueOnce(mockModels) // nano-gpt + .mockResolvedValueOnce(mockModels) // ovhcloud + .mockRejectedValueOnce(new Error("Inception API error")) // inception + .mockRejectedValueOnce(new Error("Synthetic API error")) // synthetic .mockResolvedValueOnce(mockModels) // roo .mockRejectedValueOnce(new Error("Chutes API error")) // chutes .mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm @@ -470,7 +493,6 @@ describe("webviewMessageHandler - requestRouterModels", () => { values: { provider: "unbound" }, }) - // kilocode_change start expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, @@ -484,7 +506,6 @@ describe("webviewMessageHandler - requestRouterModels", () => { error: "Synthetic API error", values: { provider: "synthetic" }, }) - // kilocode_change end expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", @@ -507,7 +528,7 @@ describe("webviewMessageHandler - requestRouterModels", () => { deepinfra: mockModels, openrouter: mockModels, requesty: {}, - glama: mockModels, // kilocode_change + glama: mockModels, unbound: {}, roo: mockModels, chutes: {}, @@ -517,36 +538,56 @@ describe("webviewMessageHandler - requestRouterModels", () => { "vercel-ai-gateway": mockModels, huggingface: {}, "io-intelligence": {}, - // kilocode_change start kilocode: mockModels, "nano-gpt": mockModels, inception: {}, synthetic: {}, - gemini: mockModels, + gemini: {}, ovhcloud: mockModels, "sap-ai-core": {}, - // kilocode_change end }, values: undefined, }) }) it("handles Error objects and string errors correctly", async () => { + // Override beforeEach mock - this test requires apiProvider to NOT be gemini or ollama + mockClineProvider.getState = vi.fn().mockResolvedValue({ + apiConfiguration: { + openRouterApiKey: "openrouter-key", + requestyApiKey: "requesty-key", + glamaApiKey: "glama-key", // kilocode_change + unboundApiKey: "unbound-key", + litellmApiKey: "litellm-key", + litellmBaseUrl: "http://localhost:4000", + // kilocode_change start + chutesApiKey: "chutes-key", + nanoGptApiKey: "nano-gpt-key", + ovhCloudAiEndpointsApiKey: "ovhcloud-key", + inceptionLabsApiKey: "inception-key", + inceptionLabsBaseUrl: "https://api.inceptionlabs.ai/v1/", + // kilocode_change end + }, + }) + // Mock providers to fail with different error types + // Order matches candidates array in webviewMessageHandler.ts: + // 1. openrouter, 2. requesty, 3. glama, 4. unbound, 5. kilocode, 6. vercel-ai-gateway, + // 7. deepinfra, 8. nano-gpt, 9. ovhcloud, 10. inception, 11. synthetic, 12. roo, 13. chutes + // Then conditional: litellm (included because litellmApiKey and litellmBaseUrl are in config) + // Note: gemini and ollama are NOT included because apiProvider is not set to those values mockGetModels - .mockRejectedValueOnce(new Error("Structured error message")) // openrouter - .mockRejectedValueOnce(new Error("Gemini API error")) // // kilocode_change: gemini + .mockRejectedValueOnce(new Error("OpenRouter API error")) // openrouter .mockRejectedValueOnce(new Error("Requesty API error")) // requesty - .mockRejectedValueOnce(new Error("Glama API error")) // kilocode_change: glama + .mockRejectedValueOnce(new Error("Glama API error")) // glama .mockRejectedValueOnce(new Error("Unbound API error")) // unbound - .mockResolvedValueOnce({}) // kilocode-openrouter - Success - .mockRejectedValueOnce(new Error("Ollama API error")) // ollama + .mockResolvedValueOnce({}) // kilocode - Success .mockRejectedValueOnce(new Error("Vercel AI Gateway error")) // vercel-ai-gateway .mockRejectedValueOnce(new Error("DeepInfra API error")) // deepinfra - .mockRejectedValueOnce(new Error("Nano-GPT API error")) // nano-gpt // kilocode_change - .mockRejectedValueOnce(new Error("OVHcloud AI Endpoints error")) // ovhcloud // kilocode_change - .mockRejectedValueOnce(new Error("Inception API error")) // kilocode_change inception - .mockRejectedValueOnce(new Error("Synthetic API error")) // kilocode_change synthetic + .mockRejectedValueOnce(new Error("Nano-GPT API error")) // nano-gpt + .mockRejectedValueOnce(new Error("OVHcloud AI Endpoints error")) // ovhcloud + .mockRejectedValueOnce(new Error("Inception API error")) // inception + .mockRejectedValueOnce(new Error("Synthetic API error")) // synthetic .mockRejectedValueOnce(new Error("Roo API error")) // roo .mockRejectedValueOnce(new Error("Chutes API error")) // chutes .mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm @@ -559,19 +600,10 @@ describe("webviewMessageHandler - requestRouterModels", () => { expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, - error: "Structured error message", + error: "OpenRouter API error", values: { provider: "openrouter" }, }) - // kilocode_change start - expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ - type: "singleRouterModelFetchResponse", - success: false, - error: "Gemini API error", - values: { provider: "gemini" }, - }) - // kilocode_change end - expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, @@ -579,14 +611,12 @@ describe("webviewMessageHandler - requestRouterModels", () => { values: { provider: "requesty" }, }) - // kilocode_change start expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, error: "Glama API error", values: { provider: "glama" }, }) - // kilocode_change end expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", @@ -595,50 +625,46 @@ describe("webviewMessageHandler - requestRouterModels", () => { values: { provider: "unbound" }, }) - // kilocode_change start expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, - error: "Ollama API error", - values: { provider: "ollama" }, + error: "Vercel AI Gateway error", + values: { provider: "vercel-ai-gateway" }, }) expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, - error: "Vercel AI Gateway error", - values: { provider: "vercel-ai-gateway" }, + error: "DeepInfra API error", + values: { provider: "deepinfra" }, }) expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, - error: "Chutes API error", - values: { provider: "chutes" }, + error: "Nano-GPT API error", + values: { provider: "nano-gpt" }, }) - // kilocode_change end expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, - error: "DeepInfra API error", - values: { provider: "deepinfra" }, + error: "OVHcloud AI Endpoints error", + values: { provider: "ovhcloud" }, }) - // kilocode_change start expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, - error: "Nano-GPT API error", - values: { provider: "nano-gpt" }, + error: "Inception API error", + values: { provider: "inception" }, }) - // kilocode_change end expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "singleRouterModelFetchResponse", success: false, - error: "Vercel AI Gateway error", - values: { provider: "vercel-ai-gateway" }, + error: "Synthetic API error", + values: { provider: "synthetic" }, }) expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ @@ -661,21 +687,6 @@ describe("webviewMessageHandler - requestRouterModels", () => { error: "LiteLLM connection failed", values: { provider: "litellm" }, }) - - // kilocode_change start - expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ - type: "singleRouterModelFetchResponse", - success: false, - error: "OVHcloud AI Endpoints error", - values: { provider: "ovhcloud" }, - }) - expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({ - type: "singleRouterModelFetchResponse", - success: false, - error: "Inception API error", - values: { provider: "inception" }, - }) - // kilocode_change end }) it("prefers config values over message values for LiteLLM", async () => { diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 47d61ce2cf2..aad6066adcf 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -72,7 +72,7 @@ import { showSystemNotification } from "../../integrations/notifications" // kil import { singleCompletionHandler } from "../../utils/single-completion-handler" // kilocode_change import { searchCommits } from "../../utils/git" import { exportSettings, importSettingsWithFeedback } from "../config/importExport" -import { getOpenAiModels } from "../../api/providers/openai" +import { getOpenAiModels, getOpenAiModelInfo } from "../../api/providers/openai" import { getVsCodeLmModels } from "../../api/providers/vscode-lm" import { openMention } from "../mentions" import { getWorkspacePath } from "../../utils/path" @@ -901,14 +901,7 @@ export const webviewMessageHandler = async ( key: "openrouter", options: { provider: "openrouter", apiKey: openRouterApiKey, baseUrl: openRouterBaseUrl }, }, - { - key: "gemini", - options: { - provider: "gemini", - apiKey: apiConfiguration.geminiApiKey, - baseUrl: apiConfiguration.googleGeminiBaseUrl, - }, - }, + { key: "requesty", options: { @@ -927,7 +920,7 @@ export const webviewMessageHandler = async ( kilocodeOrganizationId: apiConfiguration.kilocodeOrganizationId, }, }, - { key: "ollama", options: { provider: "ollama", baseUrl: apiConfiguration.ollamaBaseUrl } }, + { key: "vercel-ai-gateway", options: { provider: "vercel-ai-gateway" } }, { key: "deepinfra", @@ -1006,6 +999,26 @@ export const webviewMessageHandler = async ( }) } + // Gemini is conditional - only fetch if user is actually using Gemini provider AND has API key + if (apiConfiguration.apiProvider === "gemini" && apiConfiguration.geminiApiKey) { + candidates.push({ + key: "gemini", + options: { + provider: "gemini", + apiKey: apiConfiguration.geminiApiKey, + baseUrl: apiConfiguration.googleGeminiBaseUrl, + }, + }) + } + + // Ollama is conditional - only fetch if user is actually using Ollama provider + if (apiConfiguration.apiProvider === "ollama" && apiConfiguration.ollamaBaseUrl) { + candidates.push({ + key: "ollama", + options: { provider: "ollama", baseUrl: apiConfiguration.ollamaBaseUrl }, + }) + } + // Apply single provider filter if specified const modelFetchPromises = providerFilter ? candidates.filter(({ key }) => key === providerFilter) @@ -1161,7 +1174,7 @@ export const webviewMessageHandler = async ( break } case "requestOpenAiModels": - if (message?.values?.baseUrl && message?.values?.apiKey) { + if (message?.values?.baseUrl) { const openAiModels = await getOpenAiModels( message?.values?.baseUrl, message?.values?.apiKey, @@ -1171,6 +1184,115 @@ export const webviewMessageHandler = async ( provider.postMessageToWebview({ type: "openAiModels", openAiModels }) } + break + case "requestOpenAiModelInfo": + if (message?.values?.openAiModelId) { + let modelInfo: ReturnType + + // Primary: Try OpenRouter first (most comprehensive and up-to-date) + try { + const { getOpenRouterModels } = await import("../../api/providers/fetchers/openrouter") + // If forceRefresh is true (manual Auto-fill click), flush cache first + if (message?.values?.forceRefresh) { + await flushModels({ provider: "openrouter" }, true) + } + const openRouterModels = await getOpenRouterModels() + modelInfo = openRouterModels[message.values.openAiModelId] + + if (!modelInfo) { + const searchId = message.values.openAiModelId.toLowerCase() + // Normalize search ID by removing provider prefix + const normalizedSearchId = searchId.replace(/^[a-z-]+\//i, "") + const keys = Object.keys(openRouterModels) + // Find matches where either: + // 1. OpenRouter ID contains the search term + // 2. Normalized OpenRouter ID (without provider prefix) matches normalized search + const matches = keys.filter((id) => { + const lowerId = id.toLowerCase() + const normalizedId = lowerId.replace(/^[a-z-]+\//, "") // Remove provider prefix + return ( + lowerId.includes(searchId) || + normalizedId.includes(normalizedSearchId) || + normalizedSearchId.includes(normalizedId) + ) + }) + + if (matches.length > 0) { + // Sort by length to find the most concise match (often the base model) + matches.sort((a, b) => a.length - b.length) + modelInfo = openRouterModels[matches[0]] + } + } + } catch (error) { + console.error("Error fetching OpenRouter models for auto-fill:", error) + } + + // Merge: Get additional data from static maps (computer use, image support, etc.) + const staticModelInfo = getOpenAiModelInfo(message.values.openAiModelId) + if (modelInfo && staticModelInfo) { + // Merge static map data into OpenRouter data (static has curated capability flags) + modelInfo = { + ...modelInfo, + // Override with static map values if they provide additional capability info + supportsComputerUse: staticModelInfo.supportsComputerUse ?? modelInfo.supportsComputerUse, + supportsImages: staticModelInfo.supportsImages ?? modelInfo.supportsImages, + supportsNativeTools: staticModelInfo.supportsNativeTools ?? modelInfo.supportsNativeTools, + supportsPromptCache: staticModelInfo.supportsPromptCache ?? modelInfo.supportsPromptCache, + supportsReasoningBudget: + staticModelInfo.supportsReasoningBudget ?? modelInfo.supportsReasoningBudget, + supportsReasoningBinary: + staticModelInfo.supportsReasoningBinary ?? modelInfo.supportsReasoningBinary, + } + } else if (!modelInfo) { + // Fallback: Use static map if OpenRouter returned nothing + modelInfo = staticModelInfo + } + + // Heuristic: Auto-detect capabilities from model ID + const lowerModelId = message.values.openAiModelId.toLowerCase() + if (!modelInfo) { + modelInfo = {} as any + } + + // Only assign if modelInfo is now defined (it is, due to above check) + if (modelInfo) { + // Clone to avoid mutating shared state if it comes from a const + modelInfo = { ...modelInfo } + + if (lowerModelId.includes("computer")) { + modelInfo.supportsComputerUse = true + } + if ( + lowerModelId.includes("vision") || + lowerModelId.includes("vl") || + lowerModelId.includes("omni") || + lowerModelId.includes("gemini") || + lowerModelId.includes("gpt-4o") + ) { + modelInfo.supportsImages = true + } + if ( + lowerModelId.includes("reasoner") || + lowerModelId.includes("thinking") || + lowerModelId.includes("r1") || + lowerModelId.includes("o1") || + lowerModelId.includes("o3") + ) { + modelInfo.supportsReasoningBinary = true + } + } + + // Sanitize tiers to ensure contextWindow is not null (which breaks validation) + if (modelInfo?.tiers) { + modelInfo.tiers = modelInfo.tiers.map((tier: any) => ({ + ...tier, + contextWindow: tier.contextWindow === null ? undefined : tier.contextWindow, + })) + } + + // Always send response so UI knows the result (found or not found) + provider.postMessageToWebview({ type: "openAiModelInfo", openAiModelInfo: modelInfo }) + } break case "requestVsCodeLmModels": const vsCodeLmModels = await getVsCodeLmModels() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 5ae776219bc..be233cfa8bb 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -103,6 +103,7 @@ export interface ExtensionMessage { | "listApiConfig" | "routerModels" | "openAiModels" + | "openAiModelInfo" // kilocode_change: Auto-filled model info from known databases | "ollamaModels" | "lmStudioModels" | "vsCodeLmModels" @@ -250,6 +251,7 @@ export interface ExtensionMessage { clineMessage?: ClineMessage routerModels?: RouterModels openAiModels?: string[] + openAiModelInfo?: ModelInfo // kilocode_change: Auto-filled model info ollamaModels?: ModelRecord lmStudioModels?: ModelRecord vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 4bf05d2054f..4237661b879 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -83,6 +83,7 @@ export interface WebviewMessage { | "flushRouterModels" | "requestRouterModels" | "requestOpenAiModels" + | "requestOpenAiModelInfo" // kilocode_change: Auto-fill model info from known databases | "requestOllamaModels" | "requestLmStudioModels" | "requestRooModels" diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index ad338d342ab..b9f1b70b1cc 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -22,6 +22,7 @@ import { inputEventTransform, noTransform } from "../transforms" import { ModelPicker } from "../ModelPicker" import { R1FormatSetting } from "../R1FormatSetting" import { ThinkingBudget } from "../ThinkingBudget" +import { vscode } from "../../../utils/vscode" type OpenAICompatibleProps = { apiConfiguration: ProviderSettings @@ -107,20 +108,72 @@ export const OpenAICompatible = ({ [setApiConfigurationField], ) - const onMessage = useCallback((event: MessageEvent) => { - const message: ExtensionMessage = event.data - - switch (message.type) { - case "openAiModels": { - const updatedModels = message.openAiModels ?? [] - setOpenAiModels(Object.fromEntries(updatedModels.map((item) => [item, openAiModelInfoSaneDefaults]))) - break + const onMessage = useCallback( + (event: MessageEvent) => { + const message: ExtensionMessage = event.data + + switch (message.type) { + case "openAiModels": { + const updatedModels = message.openAiModels ?? [] + setOpenAiModels( + Object.fromEntries(updatedModels.map((item) => [item, openAiModelInfoSaneDefaults])), + ) + break + } + case "openAiModelInfo": { + if (message.openAiModelInfo) { + setApiConfigurationField("openAiCustomModelInfo", { + ...(apiConfiguration.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + ...message.openAiModelInfo, + }) + } + break + } } - } - }, []) + }, + [apiConfiguration, setApiConfigurationField], + ) useEvent("message", onMessage) + // Auto-list models when Base URL or API Key changes + // Auto-list models when Base URL or API Key changes + useEffect(() => { + const timer = setTimeout(() => { + if (apiConfiguration?.openAiBaseUrl) { + vscode.postMessage({ + type: "requestOpenAiModels", + values: { + baseUrl: apiConfiguration.openAiBaseUrl, + apiKey: apiConfiguration.openAiApiKey, + openAiHeaders: apiConfiguration.openAiHeaders, + }, + }) + } + }, 500) // Debounce 500ms + + return () => clearTimeout(timer) + }, [apiConfiguration?.openAiBaseUrl, apiConfiguration?.openAiApiKey, apiConfiguration?.openAiHeaders]) + + // Auto-fill when model ID changes + useEffect(() => { + if (apiConfiguration?.openAiModelId) { + vscode.postMessage({ + type: "requestOpenAiModelInfo", + values: { openAiModelId: apiConfiguration.openAiModelId }, + }) + } + }, [apiConfiguration?.openAiModelId]) + + const handleAutoFill = useCallback(() => { + if (apiConfiguration?.openAiModelId) { + vscode.postMessage({ + type: "requestOpenAiModelInfo", + values: { openAiModelId: apiConfiguration.openAiModelId, forceRefresh: true }, + }) + } + }, [apiConfiguration?.openAiModelId]) + return ( <>
-
- {t("settings:providers.customModel.capabilities")} +
+
+ {t("settings:providers.customModel.capabilities")} +
+
@@ -367,22 +429,78 @@ export const OpenAICompatible = ({
-
+ {/* Model Capabilities - Compact Grid */} +
+
+ ({ + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsReasoningBinary: checked, + }))}> + {t("settings:providers.customModel.supportsReasoning.label")} + + + + +
+
+ ({ + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsNativeTools: checked, + }))}> + {t("settings:providers.customModel.supportsNativeTools.label")} + + + + +
+
+ ({ + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsComputerUse: checked, + }))}> + {t("settings:providers.customModel.computerUse.label")} + + + + +
{ - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - supportsImages: checked, - } - })}> - - {t("settings:providers.customModel.imageSupport.label")} - + onChange={handleInputChange("openAiCustomModelInfo", (checked) => ({ + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsImages: checked, + }))}> + {t("settings:providers.customModel.imageSupport.label")}
-
- {t("settings:providers.customModel.imageSupport.description")} -
-
- -
{ - return { - ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), - supportsPromptCache: checked, - } - })}> - {t("settings:providers.customModel.promptCache.label")} + onChange={handleInputChange("openAiCustomModelInfo", (checked) => ({ + ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), + supportsPromptCache: checked, + }))}> + {t("settings:providers.customModel.promptCache.label")}
-
- {t("settings:providers.customModel.promptCache.description")} -
diff --git a/webview-ui/src/i18n/locales/ar/settings.json b/webview-ui/src/i18n/locales/ar/settings.json index a9633496af6..58019908839 100644 --- a/webview-ui/src/i18n/locales/ar/settings.json +++ b/webview-ui/src/i18n/locales/ar/settings.json @@ -6,7 +6,8 @@ "reset": "إعادة ضبط", "select": "اختيار", "add": "إضافة رأس", - "remove": "إزالة" + "remove": "إزالة", + "autoFill": "Auto-fill" }, "header": { "title": "الإعدادات", @@ -603,7 +604,19 @@ "description": "تكلفة كتابة للكاش أول مرة." } }, - "resetDefaults": "رجع للإعدادات الافتراضية" + "resetDefaults": "رجع للإعدادات الافتراضية", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "تحديد الحدّ الأقصى عند النهاية", diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index ecf29893c58..b0d864b6448 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -6,7 +6,8 @@ "reset": "Restablir", "select": "Seleccionar", "add": "Afegir capçalera", - "remove": "Eliminar" + "remove": "Eliminar", + "autoFill": "Auto-fill" }, "header": { "title": "Configuració", @@ -557,7 +558,19 @@ "description": "Cost per milió de tokens per escriure a la caché. Aquest és el preu cobrat quan s'emmagatzema un prompt per primera vegada." } }, - "resetDefaults": "Restablir als valors per defecte" + "resetDefaults": "Restablir als valors per defecte", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Límit de taxa al final", @@ -578,10 +591,9 @@ "none": "Cap", "minimal": "Mínim (el més ràpid)", "high": "Alt", - "xhigh": "Molt alt", + "xhigh": "Molt Alt", "medium": "Mitjà", - "low": "Baix", - "xhigh": "Molt Alt" + "low": "Baix" }, "verbosity": { "label": "Verbositat de la sortida", diff --git a/webview-ui/src/i18n/locales/cs/settings.json b/webview-ui/src/i18n/locales/cs/settings.json index 26513fdcf39..45cb91c3cf7 100644 --- a/webview-ui/src/i18n/locales/cs/settings.json +++ b/webview-ui/src/i18n/locales/cs/settings.json @@ -6,7 +6,8 @@ "reset": "Resetovat", "select": "Vybrat", "add": "Přidat záhlaví", - "remove": "Odstranit" + "remove": "Odstranit", + "autoFill": "Auto-fill" }, "header": { "title": "Nastavení", @@ -594,7 +595,19 @@ "description": "Cena za milion tokenů za zápis do mezipaměti. Toto je cena účtovaná při prvním uložení výzvy do mezipaměti." } }, - "resetDefaults": "Obnovit výchozí" + "resetDefaults": "Obnovit výchozí", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Omezení rychlosti na konci", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 0a3cda94989..80749cee8c6 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -6,7 +6,8 @@ "reset": "Zurücksetzen", "select": "Auswählen", "add": "Header hinzufügen", - "remove": "Entfernen" + "remove": "Entfernen", + "autoFill": "Auto-fill" }, "header": { "title": "Einstellungen", @@ -566,7 +567,19 @@ "description": "Kosten pro Million Tokens für das Schreiben in den Cache. Dies ist der Preis, der beim ersten Cachen eines Prompts berechnet wird." } }, - "resetDefaults": "Auf Standardwerte zurücksetzen" + "resetDefaults": "Auf Standardwerte zurücksetzen", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Ratenbegrenzung am Ende", @@ -589,8 +602,7 @@ "high": "Hoch", "xhigh": "Sehr hoch", "medium": "Mittel", - "low": "Niedrig", - "xhigh": "Sehr hoch" + "low": "Niedrig" }, "verbosity": { "label": "Ausgabe-Ausführlichkeit", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 9e487e9b312..e48c70eec27 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -6,7 +6,8 @@ "reset": "Reset", "select": "Select", "add": "Add Header", - "remove": "Remove" + "remove": "Remove", + "autoFill": "Auto-fill" }, "header": { "title": "Settings", @@ -378,9 +379,6 @@ "getZaiApiKey": "Get Z AI API Key", "zaiEntrypoint": "Z AI Entrypoint", "zaiEntrypointDescription": "Please select the appropriate API entrypoint based on your location. If you are in China, choose open.bigmodel.cn. Otherwise, choose api.z.ai.", - "minimaxApiKey": "MiniMax API Key", - "getMiniMaxApiKey": "Get MiniMax API Key", - "minimaxBaseUrl": "MiniMax Entrypoint", "geminiApiKey": "Gemini API Key", "getGroqApiKey": "Get Groq API Key", "groqApiKey": "Groq API Key", @@ -544,6 +542,18 @@ "label": "Max Output Tokens", "description": "Maximum number of tokens the model can generate in a response. (Specify -1 to allow the server to set the max tokens.)" }, + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + }, "contextWindow": { "label": "Context Window Size", "description": "Total tokens (input + output) the model can process." diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 95efa64153c..b7736aede07 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -6,7 +6,8 @@ "reset": "Restablecer", "select": "Seleccionar", "add": "Añadir encabezado", - "remove": "Eliminar" + "remove": "Eliminar", + "autoFill": "Auto-fill" }, "header": { "title": "Configuración", @@ -540,7 +541,19 @@ "description": "Costo por millón de tokens para escribir en el caché. Este es el precio que se cobra cuando se almacena un prompt en caché por primera vez." } }, - "resetDefaults": "Restablecer valores predeterminados" + "resetDefaults": "Restablecer valores predeterminados", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Límite de tasa al final", @@ -561,10 +574,9 @@ "none": "Ninguno", "minimal": "Mínimo (el más rápido)", "high": "Alto", - "xhigh": "Muy alto", + "xhigh": "Extra Alto", "medium": "Medio", - "low": "Bajo", - "xhigh": "Extra Alto" + "low": "Bajo" }, "verbosity": { "label": "Verbosidad de la salida", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 17551bd6c7f..4a7d92de770 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -6,7 +6,8 @@ "reset": "Réinitialiser", "select": "Sélectionner", "add": "Ajouter un en-tête", - "remove": "Supprimer" + "remove": "Supprimer", + "autoFill": "Auto-fill" }, "header": { "title": "Paramètres", @@ -540,7 +541,19 @@ "description": "Coût par million de tokens pour l'écriture dans le cache. C'est le prix facturé lors de la première mise en cache d'un prompt." } }, - "resetDefaults": "Réinitialiser les valeurs par défaut" + "resetDefaults": "Réinitialiser les valeurs par défaut", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Limite de débit en fin", @@ -563,8 +576,7 @@ "high": "Élevé", "xhigh": "Très élevé", "medium": "Moyen", - "low": "Faible", - "xhigh": "Très élevé" + "low": "Faible" }, "verbosity": { "label": "Verbosité de la sortie", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index adf8b7e3b02..d1a1d6d316d 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -6,7 +6,8 @@ "reset": "रीसेट करें", "select": "चुनें", "add": "हेडर जोड़ें", - "remove": "हटाएं" + "remove": "हटाएं", + "autoFill": "Auto-fill" }, "header": { "title": "सेटिंग्स", @@ -557,7 +558,19 @@ "description": "कैश में लिखने के लिए प्रति मिलियन टोकन की लागत। यह वह मूल्य है जो पहली बार प्रॉम्प्ट को कैश करने पर लगाया जाता है।" } }, - "resetDefaults": "डिफ़ॉल्ट पर रीसेट करें" + "resetDefaults": "डिफ़ॉल्ट पर रीसेट करें", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "अंत में दर सीमा", @@ -578,10 +591,9 @@ "none": "कोई नहीं", "minimal": "न्यूनतम (सबसे तेज़)", "high": "उच्च", - "xhigh": "बहुत उच्च", + "xhigh": "अत्यंत उच्च", "medium": "मध्यम", - "low": "निम्न", - "xhigh": "अत्यंत उच्च" + "low": "निम्न" }, "verbosity": { "label": "आउटपुट वर्बोसिटी", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index c8a9423af68..d3c52a731ef 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -6,7 +6,8 @@ "reset": "Reset", "select": "Pilih", "add": "Tambah Header", - "remove": "Hapus" + "remove": "Hapus", + "autoFill": "Auto-fill" }, "header": { "title": "Pengaturan", @@ -557,7 +558,19 @@ "description": "Biaya per juta token untuk menulis ke cache. Ini adalah harga yang dikenakan ketika prompt di-cache untuk pertama kalinya." } }, - "resetDefaults": "Reset ke Default" + "resetDefaults": "Reset ke Default", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Batas laju di akhir", @@ -578,10 +591,9 @@ "none": "Tidak Ada", "minimal": "Minimal (Tercepat)", "high": "Tinggi", - "xhigh": "Sangat tinggi", + "xhigh": "Ekstra Tinggi", "medium": "Sedang", - "low": "Rendah", - "xhigh": "Ekstra Tinggi" + "low": "Rendah" }, "verbosity": { "label": "Verbositas Output", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 779e6fcbdd8..05a75cebbd9 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -6,7 +6,8 @@ "reset": "Ripristina", "select": "Seleziona", "add": "Aggiungi intestazione", - "remove": "Rimuovi" + "remove": "Rimuovi", + "autoFill": "Auto-fill" }, "header": { "title": "Impostazioni", @@ -572,7 +573,19 @@ "description": "Costo per milione di token per scrivere nella cache. Questo prezzo viene applicato quando si memorizza un prompt nella cache per la prima volta." } }, - "resetDefaults": "Ripristina valori predefiniti" + "resetDefaults": "Ripristina valori predefiniti", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Limite di velocità alla fine", @@ -593,10 +606,9 @@ "none": "Nessuno", "minimal": "Minimo (più veloce)", "high": "Alto", - "xhigh": "Molto alto", + "xhigh": "Extra Alto", "medium": "Medio", - "low": "Basso", - "xhigh": "Extra Alto" + "low": "Basso" }, "verbosity": { "label": "Verbosity dell'output", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index b35add6fcc7..9827cf6cc50 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -6,7 +6,8 @@ "reset": "リセット", "select": "選択", "add": "ヘッダーを追加", - "remove": "削除" + "remove": "削除", + "autoFill": "Auto-fill" }, "header": { "title": "設定", @@ -563,7 +564,19 @@ "description": "キャッシュへの書き込みの100万トークンあたりのコスト。これはプロンプトが初めてキャッシュされる際に課金される価格です。" } }, - "resetDefaults": "デフォルトにリセット" + "resetDefaults": "デフォルトにリセット", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "終了時のレート制限", @@ -584,10 +597,9 @@ "none": "なし", "minimal": "最小 (最速)", "high": "高", - "xhigh": "非常に高い", + "xhigh": "極高", "medium": "中", - "low": "低", - "xhigh": "極高" + "low": "低" }, "verbosity": { "label": "出力の冗長性", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index ffb6c92024a..88977a8c837 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -6,7 +6,8 @@ "reset": "초기화", "select": "선택", "add": "헤더 추가", - "remove": "삭제" + "remove": "삭제", + "autoFill": "Auto-fill" }, "header": { "title": "설정", @@ -557,7 +558,19 @@ "description": "캐시에 쓰기의 백만 토큰당 비용입니다. 이는 프롬프트가 처음 캐시될 때 청구되는 가격입니다." } }, - "resetDefaults": "기본값으로 재설정" + "resetDefaults": "기본값으로 재설정", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "종료 시 속도 제한", @@ -580,8 +593,7 @@ "high": "높음", "xhigh": "매우 높음", "medium": "중간", - "low": "낮음", - "xhigh": "매우 높음" + "low": "낮음" }, "verbosity": { "label": "출력 상세도", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 90d01fb4fe9..4314148eb4a 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -6,7 +6,8 @@ "reset": "Resetten", "select": "Selecteren", "add": "Header toevoegen", - "remove": "Verwijderen" + "remove": "Verwijderen", + "autoFill": "Auto-fill" }, "header": { "title": "Instellingen", @@ -557,7 +558,19 @@ "description": "Kosten per miljoen tokens voor het schrijven naar de cache. Dit is de prijs die wordt gerekend wanneer een prompt voor het eerst wordt gecachet." } }, - "resetDefaults": "Standaardwaarden herstellen" + "resetDefaults": "Standaardwaarden herstellen", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Snelheidslimiet aan het eind", @@ -578,10 +591,9 @@ "none": "Geen", "minimal": "Minimaal (Snelst)", "high": "Hoog", - "xhigh": "Zeer hoog", + "xhigh": "Extra Hoog", "medium": "Middel", - "low": "Laag", - "xhigh": "Extra Hoog" + "low": "Laag" }, "verbosity": { "label": "Uitvoerbaarheid", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index a4484a5389d..3d9a19a1363 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -6,7 +6,8 @@ "reset": "Resetuj", "select": "Wybierz", "add": "Dodaj nagłówek", - "remove": "Usuń" + "remove": "Usuń", + "autoFill": "Auto-fill" }, "header": { "title": "Ustawienia", @@ -557,7 +558,19 @@ "description": "Koszt za milion tokenów za zapis do bufora. Ta cena jest naliczana przy pierwszym buforowaniu podpowiedzi." } }, - "resetDefaults": "Przywróć domyślne" + "resetDefaults": "Przywróć domyślne", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Limit prędkości na końcu", @@ -580,8 +593,7 @@ "high": "Wysoki", "xhigh": "Bardzo wysoki", "medium": "Średni", - "low": "Niski", - "xhigh": "Bardzo wysoki" + "low": "Niski" }, "verbosity": { "label": "Szczegółowość danych wyjściowych", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 0b826365bf7..429a9bf8887 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -6,7 +6,8 @@ "reset": "Redefinir", "select": "Selecionar", "add": "Adicionar cabeçalho", - "remove": "Remover" + "remove": "Remover", + "autoFill": "Auto-fill" }, "header": { "title": "Configurações", @@ -531,7 +532,19 @@ "description": "Custo por milhão de tokens para escrita no cache. Este é o preço cobrado quando um prompt é armazenado em cache pela primeira vez." } }, - "resetDefaults": "Restaurar Padrões" + "resetDefaults": "Restaurar Padrões", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Limite de taxa no fim", @@ -552,10 +565,9 @@ "none": "Nenhum", "minimal": "Mínimo (mais rápido)", "high": "Alto", - "xhigh": "Muito alto", + "xhigh": "Extra Alto", "medium": "Médio", - "low": "Baixo", - "xhigh": "Extra Alto" + "low": "Baixo" }, "verbosity": { "label": "Verbosidade da saída", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 4a7f783c049..4dc840b3788 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -6,7 +6,8 @@ "reset": "Сбросить", "select": "Выбрать", "add": "Добавить заголовок", - "remove": "Удалить" + "remove": "Удалить", + "autoFill": "Auto-fill" }, "header": { "title": "Настройки", @@ -557,7 +558,19 @@ "description": "Стоимость за миллион токенов при записи в кэш. Взимается при первом кэшировании подсказки." } }, - "resetDefaults": "Сбросить к значениям по умолчанию" + "resetDefaults": "Сбросить к значениям по умолчанию", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Ограничение скорости в конце", @@ -580,8 +593,7 @@ "high": "Высокие", "xhigh": "Очень высокие", "medium": "Средние", - "low": "Низкие", - "xhigh": "Очень высокие" + "low": "Низкие" }, "verbosity": { "label": "Подробность вывода", diff --git a/webview-ui/src/i18n/locales/th/settings.json b/webview-ui/src/i18n/locales/th/settings.json index 128d387bee1..9965d034a4c 100644 --- a/webview-ui/src/i18n/locales/th/settings.json +++ b/webview-ui/src/i18n/locales/th/settings.json @@ -6,7 +6,8 @@ "reset": "รีเซ็ต", "select": "เลือก", "add": "เพิ่ม Header", - "remove": "ลบ" + "remove": "ลบ", + "autoFill": "Auto-fill" }, "header": { "title": "การตั้งค่า", @@ -564,7 +565,19 @@ "description": "ค่าใช้จ่ายต่อล้านโทเค็นสำหรับการเขียนไปยังแคช นี่คือราคาที่เรียกเก็บเมื่อมีการแคชพรอมต์เป็นครั้งแรก" } }, - "resetDefaults": "รีเซ็ตเป็นค่าเริ่มต้น" + "resetDefaults": "รีเซ็ตเป็นค่าเริ่มต้น", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "จำกัดอัตราที่จุดสิ้นสุด", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index d7923a9d0d6..016d0bc3171 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -6,7 +6,8 @@ "reset": "Sıfırla", "select": "Seç", "add": "Başlık Ekle", - "remove": "Kaldır" + "remove": "Kaldır", + "autoFill": "Auto-fill" }, "header": { "title": "Ayarlar", @@ -532,7 +533,19 @@ "description": "Önbelleğe yazma başına milyon token maliyeti. Bu, bir istem ilk kez önbelleğe alındığında uygulanan fiyattır." } }, - "resetDefaults": "Varsayılanlara Sıfırla" + "resetDefaults": "Varsayılanlara Sıfırla", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Bitişte oran sınırı", @@ -553,10 +566,9 @@ "none": "Yok", "minimal": "Minimal (en hızlı)", "high": "Yüksek", - "xhigh": "Çok yüksek", + "xhigh": "Ekstra Yüksek", "medium": "Orta", - "low": "Düşük", - "xhigh": "Ekstra Yüksek" + "low": "Düşük" }, "verbosity": { "label": "Çıktı Ayrıntı Düzeyi", diff --git a/webview-ui/src/i18n/locales/uk/settings.json b/webview-ui/src/i18n/locales/uk/settings.json index 5a999828dd4..6f982c86e92 100644 --- a/webview-ui/src/i18n/locales/uk/settings.json +++ b/webview-ui/src/i18n/locales/uk/settings.json @@ -6,7 +6,8 @@ "reset": "Скинути", "select": "Вибрати", "add": "Додати заголовок", - "remove": "Видалити" + "remove": "Видалити", + "autoFill": "Auto-fill" }, "header": { "title": "Налаштування", @@ -602,7 +603,19 @@ "description": "Вартість за мільйон токенів для запису в кеш. Це ціна, що стягується, коли підказка кешується вперше." } }, - "resetDefaults": "Скинути до стандартних" + "resetDefaults": "Скинути до стандартних", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Обмеження швидкості в кінці", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 7c104bde29b..764bc3dc796 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -6,7 +6,8 @@ "reset": "Đặt lại", "select": "Chọn", "add": "Thêm tiêu đề", - "remove": "Xóa" + "remove": "Xóa", + "autoFill": "Auto-fill" }, "header": { "title": "Cài đặt", @@ -557,7 +558,19 @@ "description": "Chi phí cho mỗi triệu token khi ghi vào bộ nhớ đệm. Đây là giá được tính khi một lời nhắc được lưu vào bộ nhớ đệm lần đầu tiên." } }, - "resetDefaults": "Đặt lại về mặc định" + "resetDefaults": "Đặt lại về mặc định", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "Giới hạn tỷ lệ ở cuối", @@ -580,8 +593,7 @@ "high": "Cao", "xhigh": "Rất cao", "medium": "Trung bình", - "low": "Thấp", - "xhigh": "Rất cao" + "low": "Thấp" }, "verbosity": { "label": "Mức độ chi tiết đầu ra", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 419198ec64b..8ca8595b744 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -6,7 +6,8 @@ "reset": "恢复默认设置", "select": "选择", "add": "添加标头", - "remove": "移除" + "remove": "移除", + "autoFill": "Auto-fill" }, "header": { "title": "设置", @@ -557,7 +558,19 @@ "description": "向缓存写入每百万Token的成本。这是首次缓存提示时收取的费用。" } }, - "resetDefaults": "重置为默认值" + "resetDefaults": "重置为默认值", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "在结束时速率限制", @@ -578,10 +591,9 @@ "none": "无", "minimal": "最小 (最快)", "high": "高", - "xhigh": "超高", + "xhigh": "极高", "medium": "中", - "low": "低", - "xhigh": "极高" + "low": "低" }, "verbosity": { "label": "输出详细程度", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 9746890711b..61da948cb54 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -6,7 +6,8 @@ "reset": "重設", "select": "選擇", "add": "新增標頭", - "remove": "移除" + "remove": "移除", + "autoFill": "Auto-fill" }, "header": { "title": "設定", @@ -532,7 +533,19 @@ "description": "每百萬 Token 的快取寫入費用。當提示首次被儲存至快取時,會收取此費用。" } }, - "resetDefaults": "重設為預設值" + "resetDefaults": "重設為預設值", + "supportsReasoning": { + "label": "Supports Reasoning (e.g. o1/DeepSeek)", + "description": "Enable this if your model supports reasoning capabilities (e.g. 'think' tags)." + }, + "supportsNativeTools": { + "label": "Supports Function Calling", + "description": "Enable this if your model supports native function calling (tools)." + }, + "supportsComputerUse": { + "label": "Supports Computer Use", + "description": "Enable this if your model supports computer use/browser interaction." + } }, "rateLimitAfter": { "label": "在結束時速率限制", @@ -555,8 +568,7 @@ "high": "高", "xhigh": "超高", "medium": "中", - "low": "低", - "xhigh": "超高" + "low": "低" }, "verbosity": { "label": "輸出詳細程度",