diff --git a/packages/types/src/__tests__/provider-settings.test.ts b/packages/types/src/__tests__/provider-settings.test.ts index cd786a6529..77e7527b40 100644 --- a/packages/types/src/__tests__/provider-settings.test.ts +++ b/packages/types/src/__tests__/provider-settings.test.ts @@ -166,3 +166,75 @@ describe("getApiProtocol", () => { }) }) }) + +describe("openAiToolStrictMode", () => { + it("should be optional and absent by default", () => { + const result = providerSettingsSchemaDiscriminated.parse({ + apiProvider: "openai", + openAiModelId: "test-model", + }) + expect(result.apiProvider).toBe("openai") + if (result.apiProvider === "openai") { + expect(result.openAiToolStrictMode).toBeUndefined() + } + }) + + it("should accept true when provided", () => { + const result = providerSettingsSchemaDiscriminated.parse({ + apiProvider: "openai", + openAiModelId: "test-model", + openAiToolStrictMode: true, + }) + expect(result.apiProvider).toBe("openai") + if (result.apiProvider === "openai") { + expect(result.openAiToolStrictMode).toBe(true) + } + }) + + it("should accept false when provided", () => { + const result = providerSettingsSchemaDiscriminated.parse({ + apiProvider: "openai", + openAiModelId: "test-model", + openAiToolStrictMode: false, + }) + expect(result.apiProvider).toBe("openai") + if (result.apiProvider === "openai") { + expect(result.openAiToolStrictMode).toBe(false) + } + }) + + it("should not break existing profile deserialization when absent", () => { + const existingProfile = { + apiProvider: "openai" as const, + openAiBaseUrl: "https://api.example.com/v1", + openAiApiKey: "sk-test", + openAiModelId: "gpt-4", + openAiStreamingEnabled: true, + } + const result = providerSettingsSchemaDiscriminated.parse(existingProfile) + expect(result.apiProvider).toBe("openai") + if (result.apiProvider === "openai") { + expect(result.openAiModelId).toBe("gpt-4") + expect(result.openAiToolStrictMode).toBeUndefined() + } + }) + + it("should only exist on the openai (OpenAI Compatible) provider profile", () => { + const openAiResult = providerSettingsSchemaDiscriminated.parse({ + apiProvider: "openai", + openAiToolStrictMode: true, + }) + expect(openAiResult.apiProvider).toBe("openai") + if (openAiResult.apiProvider === "openai") { + expect(openAiResult.openAiToolStrictMode).toBe(true) + } + + // Anthropic provider should not have this field + const anthropicResult = providerSettingsSchemaDiscriminated.parse({ + apiProvider: "anthropic", + apiKey: "sk-test", + }) + expect(anthropicResult.apiProvider).toBe("anthropic") + expect((anthropicResult as Record).openAiToolStrictMode).toBeUndefined() + }) +}) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index e17cd5ddbc..8cd868f297 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -248,6 +248,7 @@ const openAiSchema = baseProviderSettingsSchema.extend({ openAiStreamingEnabled: z.boolean().optional(), openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. openAiHeaders: z.record(z.string(), z.string()).optional(), + openAiToolStrictMode: z.boolean().optional(), // Profile-scoped strict function-tool schema toggle for OpenAI Compatible provider. Absent = false (backward compatible). }) const ollamaSchema = baseProviderSettingsSchema.extend({ diff --git a/progress.txt b/progress.txt deleted file mode 100644 index b3983826b3..0000000000 --- a/progress.txt +++ /dev/null @@ -1,59 +0,0 @@ -# Reapplication Progress — rc6 branch cleanup -# Updated: 2026-02-15 - -## Completed Batches - -### Batch 1 — Clean cherry-picks (PR #11473) -- 22 PRs merged cleanly -- Status: MERGED to main - -### Batch 2 — Minor conflicts (PR #11474) -- 9 PRs with minor conflicts resolved -- Status: MERGED to main - -### Batch 3 — Skills Infrastructure & Browser Use Removal (4 PRs) -- PR #11102: skill mode dropdown (44 conflicts resolved) -- PR #11157: improve Skills/Slash Commands UI (6 conflicts resolved) -- PR #11414: remove built-in skills mechanism (4 conflicts resolved) -- PR #11392: remove browser use entirely (5 conflicts resolved) -- Status: ON BRANCH reapply/batch-3-4-5-major-conflicts - -### Batch 4 — Provider Removals (2 PRs) -- PR #11253: remove URL context/Grounding checkboxes (4 conflicts resolved) -- PR #11297: remove 9 low-usage providers + retired UX (14 conflicts resolved) -- Status: ON BRANCH reapply/batch-3-4-5-major-conflicts - -### Batch 5 — Azure Foundry -- PR #11315 and #11374: EXCLUDED — depends on AI-SDK (@ai-sdk/azure, from "ai") -- These PRs are AI-SDK-entangled and cannot be cherry-picked to the pre-AI-SDK codebase -- Status: DEFERRED (AI-SDK dependent) - -## Post-cherry-pick Fixes Applied -1. Restored gemini.ts + vertex.ts to pre-AI-SDK state (cherry-picks brought AI-SDK versions) -2. Restored ai-sdk.spec.ts, gemini-handler.spec.ts, vertex.spec.ts to pre-AI-SDK versions -3. Fixed processUserContentMentions.ts ghost import (rooMessage.ts doesn't exist) -4. Added missing skills type exports to @roo-code/types (SkillMetadata, validateSkillName, etc.) -5. Added SkillsSettings import to SettingsView.tsx -6. Added Dialog/Select/Collapsible mocks to SettingsView test files -7. Fixed Task.ts type mismatches (replaced local types with Anthropic SDK types) -8. Added skills state to ExtensionStateContext - -## Deferred PRs (AI-SDK Entangled) -- #11379: delegation (AI-SDK) -- #11418: delegation (AI-SDK) -- #11422: delegation (AI-SDK) -- #11315: Azure Foundry provider (AI-SDK) -- #11374: Azure Foundry fix (AI-SDK) - -## Validation Results -- Backend tests: ALL PASSED (5224 tests) -- UI tests: ALL PASSED (1267 tests) -- Type checks: ALL PASSED (14/14 packages) -- AI-SDK contamination: CLEAN (0 matches) - -## Notes -- Pre-push hook fails on `roo-cline:bundle` because `generate-built-in-skills.ts` was removed - by PR #11414 but `package.json` still references it in `prebundle`. This is expected and - will be resolved when the PR is merged to main and the script reference is cleaned up. -- Push was done with `--no-verify` after independent verification of types, backend tests, - and UI tests all passed cleanly. diff --git a/src/api/providers/__tests__/base-provider.spec.ts b/src/api/providers/__tests__/base-provider.spec.ts index ced452f5a5..66109e7cf3 100644 --- a/src/api/providers/__tests__/base-provider.spec.ts +++ b/src/api/providers/__tests__/base-provider.spec.ts @@ -28,8 +28,8 @@ class TestProvider extends BaseProvider { } // Expose protected method for testing - public testConvertToolsForOpenAI(tools: any[] | undefined): any[] | undefined { - return this.convertToolsForOpenAI(tools) + public testConvertToolsForOpenAI(tools: any[] | undefined, strictMode: boolean = false): any[] | undefined { + return this.convertToolsForOpenAI(tools, strictMode) } } @@ -176,6 +176,16 @@ describe("BaseProvider", () => { expect(result.additionalProperties).toBe(false) expect(result.required).toEqual([]) }) + + it("should add empty properties and required arrays to zero-argument object schemas", () => { + const result = provider.testConvertToolSchemaForOpenAI({ type: "object" }) + + expect(result).toMatchObject({ + additionalProperties: false, + properties: {}, + required: [], + }) + }) }) describe("convertToolsForOpenAI", () => { @@ -184,100 +194,230 @@ describe("BaseProvider", () => { expect(result).toBeUndefined() }) - it("should set strict: true for non-MCP tools", () => { + it("should preserve non-function tools unchanged", () => { const tools = [ { - type: "function", - function: { - name: "read_file", - description: "Read a file", - parameters: { type: "object", properties: {} }, - }, + type: "other_type", + data: "some data", }, ] const result = provider.testConvertToolsForOpenAI(tools) - expect(result?.[0].function.strict).toBe(true) + expect(result?.[0]).toEqual(tools[0]) }) - it("should set strict: false for MCP tools (mcp-- prefix)", () => { - const tools = [ - { - type: "function", - function: { - name: "mcp--github--get_me", - description: "Get current user", - parameters: { type: "object", properties: {} }, + describe("strictMode = false (default)", () => { + it("should set strict: false for non-MCP tools", () => { + const tools = [ + { + type: "function", + function: { + name: "read_file", + description: "Read a file", + parameters: { type: "object", properties: {} }, + }, }, - }, - ] + ] - const result = provider.testConvertToolsForOpenAI(tools) + const result = provider.testConvertToolsForOpenAI(tools) - expect(result?.[0].function.strict).toBe(false) - }) + expect(result?.[0].function.strict).toBe(false) + }) - it("should apply schema conversion to non-MCP tools", () => { - const tools = [ - { - type: "function", - function: { - name: "read_file", - description: "Read a file", - parameters: { - type: "object", - properties: { - path: { type: "string" }, + it("should preserve original best-effort schema for non-MCP tools (no hardening)", () => { + const tools = [ + { + type: "function", + function: { + name: "read_file", + description: "Read a file", + parameters: { + type: "object", + properties: { + path: { type: "string" }, + encoding: { type: ["string", "null"] }, + }, + // Note: no required array, no additionalProperties }, }, }, - }, - ] + ] + + const result = provider.testConvertToolsForOpenAI(tools) + + // Schema should NOT be hardened when strict is false + expect(result?.[0].function.parameters.additionalProperties).toBeUndefined() + expect(result?.[0].function.parameters.required).toBeUndefined() + // Nullable type should be preserved as-is + expect(result?.[0].function.parameters.properties.encoding.type).toEqual(["string", "null"]) + }) + + it("should set strict: false for MCP tools", () => { + const tools = [ + { + type: "function", + function: { + name: "mcp--github--get_me", + description: "Get current user", + parameters: { type: "object", properties: {} }, + }, + }, + ] - const result = provider.testConvertToolsForOpenAI(tools) + const result = provider.testConvertToolsForOpenAI(tools) - expect(result?.[0].function.parameters.additionalProperties).toBe(false) - expect(result?.[0].function.parameters.required).toEqual(["path"]) - }) + expect(result?.[0].function.strict).toBe(false) + }) - it("should not apply schema conversion to MCP tools in base-provider", () => { - // Note: In base-provider, MCP tools are passed through unchanged - // The openai-native provider has its own handling for MCP tools - const tools = [ - { - type: "function", - function: { - name: "mcp--github--get_me", - description: "Get current user", - parameters: { - type: "object", - properties: { - token: { type: "string" }, + it("should preserve original schema for MCP tools (no hardening)", () => { + const tools = [ + { + type: "function", + function: { + name: "mcp--github--get_me", + description: "Get current user", + parameters: { + type: "object", + properties: { + token: { type: "string" }, + }, + required: ["token"], }, - required: ["token"], }, }, - }, - ] + ] - const result = provider.testConvertToolsForOpenAI(tools) + const result = provider.testConvertToolsForOpenAI(tools) - // MCP tools pass through original parameters in base-provider - expect(result?.[0].function.parameters.additionalProperties).toBeUndefined() + expect(result?.[0].function.parameters.additionalProperties).toBeUndefined() + expect(result?.[0].function.parameters.required).toEqual(["token"]) + }) }) - it("should preserve non-function tools unchanged", () => { - const tools = [ - { - type: "other_type", - data: "some data", - }, - ] + describe("strictMode = true", () => { + it("should set strict: true for non-MCP tools", () => { + const tools = [ + { + type: "function", + function: { + name: "read_file", + description: "Read a file", + parameters: { type: "object", properties: {} }, + }, + }, + ] - const result = provider.testConvertToolsForOpenAI(tools) + const result = provider.testConvertToolsForOpenAI(tools, true) - expect(result?.[0]).toEqual(tools[0]) + expect(result?.[0].function.strict).toBe(true) + }) + + it("should apply schema hardening to non-MCP tools", () => { + const tools = [ + { + type: "function", + function: { + name: "read_file", + description: "Read a file", + parameters: { + type: "object", + properties: { + path: { type: "string" }, + }, + }, + }, + }, + ] + + const result = provider.testConvertToolsForOpenAI(tools, true) + + expect(result?.[0].function.parameters.additionalProperties).toBe(false) + expect(result?.[0].function.parameters.required).toEqual(["path"]) + }) + + it("should harden nested objects and arrays in non-MCP tools", () => { + const tools = [ + { + type: "function", + function: { + name: "create_user", + description: "Create a user", + parameters: { + type: "object", + properties: { + user: { + type: "object", + properties: { + name: { type: "string" }, + }, + }, + tags: { + type: "array", + items: { + type: "object", + properties: { + label: { type: "string" }, + }, + }, + }, + }, + }, + }, + }, + ] + + const result = provider.testConvertToolsForOpenAI(tools, true) + + expect(result?.[0].function.parameters.additionalProperties).toBe(false) + expect(result?.[0].function.parameters.properties.user.additionalProperties).toBe(false) + expect(result?.[0].function.parameters.properties.tags.items.additionalProperties).toBe(false) + }) + + it("should ALWAYS set strict: false for MCP tools even when strictMode is true", () => { + const tools = [ + { + type: "function", + function: { + name: "mcp--github--get_me", + description: "Get current user", + parameters: { type: "object", properties: {} }, + }, + }, + ] + + const result = provider.testConvertToolsForOpenAI(tools, true) + + expect(result?.[0].function.strict).toBe(false) + }) + + it("should preserve original schema for MCP tools even when strictMode is true", () => { + const tools = [ + { + type: "function", + function: { + name: "mcp--github--get_me", + description: "Get current user", + parameters: { + type: "object", + properties: { + token: { type: "string" }, + optional_param: { type: ["string", "null"] }, + }, + required: ["token"], + }, + }, + }, + ] + + const result = provider.testConvertToolsForOpenAI(tools, true) + + // MCP schema should NOT be hardened + expect(result?.[0].function.parameters.additionalProperties).toBeUndefined() + expect(result?.[0].function.parameters.required).toEqual(["token"]) + // Nullable type preserved + expect(result?.[0].function.parameters.properties.optional_param.type).toEqual(["string", "null"]) + }) }) }) }) diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index e8146a999a..ef319811ed 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -885,7 +885,6 @@ describe("OpenAiHandler", () => { // No custom temperature set → `temperature` is omitted. tools: undefined, tool_choice: undefined, - parallel_tool_calls: true, }, { path: "/models/chat/completions" }, ) @@ -893,6 +892,7 @@ describe("OpenAiHandler", () => { // Verify max_tokens is NOT included when not explicitly set const callArgs = mockCreate.mock.calls[0][0] expect(callArgs).not.toHaveProperty("max_completion_tokens") + expect(callArgs).not.toHaveProperty("parallel_tool_calls") }) it("should handle non-streaming responses with Azure AI Inference Service", async () => { @@ -931,7 +931,6 @@ describe("OpenAiHandler", () => { ], tools: undefined, tool_choice: undefined, - parallel_tool_calls: true, }, { path: "/models/chat/completions" }, ) @@ -939,6 +938,7 @@ describe("OpenAiHandler", () => { // Verify max_tokens is NOT included when not explicitly set const callArgs = mockCreate.mock.calls[0][0] expect(callArgs).not.toHaveProperty("max_completion_tokens") + expect(callArgs).not.toHaveProperty("parallel_tool_calls") }) it("should handle completePrompt with Azure AI Inference Service", async () => { @@ -1014,6 +1014,7 @@ describe("OpenAiHandler", () => { it("should handle O3 model with streaming and include max_completion_tokens when includeMaxTokens is true", async () => { const o3Handler = new OpenAiHandler({ ...o3Options, + reasoningEffort: "high", includeMaxTokens: true, modelMaxTokens: 32000, modelTemperature: 0.5, @@ -1041,7 +1042,7 @@ describe("OpenAiHandler", () => { ], stream: true, stream_options: { include_usage: true }, - reasoning_effort: "medium", + reasoning_effort: "high", temperature: undefined, // O3 models do not support deprecated max_tokens but do support max_completion_tokens max_completion_tokens: 32000, @@ -1200,6 +1201,7 @@ describe("OpenAiHandler", () => { it("should handle O3 model non-streaming with reasoning_effort and max_completion_tokens when includeMaxTokens is true", async () => { const o3Handler = new OpenAiHandler({ ...o3Options, + reasoningEffort: "high", openAiStreamingEnabled: false, includeMaxTokens: true, modelTemperature: 0.3, @@ -1225,7 +1227,7 @@ describe("OpenAiHandler", () => { }, { role: "user", content: "Hello!" }, ], - reasoning_effort: "medium", + reasoning_effort: "high", temperature: undefined, // O3 models do not support deprecated max_tokens but do support max_completion_tokens max_completion_tokens: 65536, // Using default maxTokens from o3Options diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index f4928b0b0a..b9ddea3c8c 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -93,9 +93,14 @@ export abstract class BaseOpenAiCompatibleProvider messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)], stream: true, stream_options: { include_usage: true }, - tools: this.convertToolsForOpenAI(metadata?.tools), + tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false), tool_choice: metadata?.tool_choice, - parallel_tool_calls: metadata?.parallelToolCalls ?? true, + // Only send parallel_tool_calls when tools are present; some + // OpenAI-compatible providers (e.g. Upstage solar-open2) reject + // this field when no tools are supplied. + ...(metadata?.tools && metadata.tools.length > 0 + ? { parallel_tool_calls: metadata?.parallelToolCalls ?? true } + : {}), } // Add thinking parameter if reasoning is enabled and model supports it diff --git a/src/api/providers/base-provider.ts b/src/api/providers/base-provider.ts index 89366fb619..de25ad3c8f 100644 --- a/src/api/providers/base-provider.ts +++ b/src/api/providers/base-provider.ts @@ -23,11 +23,23 @@ export abstract class BaseProvider implements ApiHandler { abstract getModel(): { id: string; info: ModelInfo } /** - * Converts an array of tools to be compatible with OpenAI's strict mode. - * Filters for function tools, applies schema conversion to their parameters, - * and ensures all tools have consistent strict: true values. + * Converts an array of tools for OpenAI-compatible providers. + * Filters for function tools and applies schema conversion to their parameters. + * + * When `strictMode` is true, non-MCP function tools get `strict: true` and + * their schemas are hardened via `convertToolSchemaForOpenAI()` (adds + * `additionalProperties: false`, marks all properties required, etc.). + * + * When `strictMode` is false (default), non-MCP function tools get + * `strict: false` and their original best-effort schemas are preserved + * without hardening. This is semantically consistent: `strict: false` + * should not imply strict-schema transformations. + * + * MCP tools are ALWAYS `strict: false` with original parameters preserved, + * regardless of the `strictMode` setting, because MCP schemas may contain + * optional properties that must remain optional. */ - protected convertToolsForOpenAI(tools: any[] | undefined): any[] | undefined { + protected convertToolsForOpenAI(tools: any[] | undefined, strictMode: boolean = false): any[] | undefined { if (!tools) { return undefined } @@ -37,18 +49,40 @@ export abstract class BaseProvider implements ApiHandler { return tool } - // MCP tools use the 'mcp--' prefix - disable strict mode for them + // MCP tools use the 'mcp--' prefix - always disable strict mode // to preserve optional parameters from the MCP server schema const isMcp = isMcpTool(tool.function.name) + if (isMcp) { + return { + ...tool, + function: { + ...tool.function, + strict: false, + parameters: tool.function.parameters, + }, + } + } + + // Non-MCP function tools respect the strictMode setting + if (strictMode) { + return { + ...tool, + function: { + ...tool.function, + strict: true, + parameters: this.convertToolSchemaForOpenAI(tool.function.parameters), + }, + } + } + + // strictMode false: preserve original best-effort schema return { ...tool, function: { ...tool.function, - strict: !isMcp, - parameters: isMcp - ? tool.function.parameters - : this.convertToolSchemaForOpenAI(tool.function.parameters), + strict: false, + parameters: tool.function.parameters, }, } }) @@ -76,6 +110,11 @@ export abstract class BaseProvider implements ApiHandler { result.additionalProperties = false } + if (result.properties === undefined) { + result.properties = {} + result.required = [] + } + if (result.properties) { const allKeys = Object.keys(result.properties) // OpenAI strict mode requires ALL properties to be in required array diff --git a/src/api/providers/deepseek.ts b/src/api/providers/deepseek.ts index 2e85c016b0..023fc929fd 100644 --- a/src/api/providers/deepseek.ts +++ b/src/api/providers/deepseek.ts @@ -155,7 +155,7 @@ export class DeepSeekHandler extends OpenAiHandler { stream_options: { include_usage: true }, ...(thinking && { thinking }), ...(deepSeekReasoningEffort && { reasoning_effort: deepSeekReasoningEffort }), - tools: this.convertToolsForOpenAI(metadata?.tools), + tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false), tool_choice: metadata?.tool_choice, parallel_tool_calls: metadata?.parallelToolCalls ?? true, } diff --git a/src/api/providers/friendli.ts b/src/api/providers/friendli.ts index a5507e355a..8507c58ba7 100644 --- a/src/api/providers/friendli.ts +++ b/src/api/providers/friendli.ts @@ -169,7 +169,7 @@ export class FriendliHandler extends BaseOpenAiCompatibleProvider 0 + ? { parallel_tool_calls: metadata?.parallelToolCalls ?? true } + : {}), } // Add max_tokens if needed @@ -231,9 +236,14 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl ? convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) : [systemMessage, ...convertToOpenAiMessages(messages)], // Tools are always present (minimum ALWAYS_AVAILABLE_TOOLS) - tools: this.convertToolsForOpenAI(metadata?.tools), + tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false), tool_choice: metadata?.tool_choice, - parallel_tool_calls: metadata?.parallelToolCalls ?? true, + // Only send parallel_tool_calls when tools are present; some + // OpenAI-compatible providers (e.g. Upstage solar-open2) reject + // this field when no tools are supplied. + ...(metadata?.tools && metadata.tools.length > 0 + ? { parallel_tool_calls: metadata?.parallelToolCalls ?? true } + : {}), } // Add max_tokens if needed @@ -342,7 +352,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl messages: Anthropic.Messages.MessageParam[], metadata?: ApiHandlerCreateMessageMetadata, ): ApiStream { - const modelInfo = this.getModel().info + const { info: modelInfo, reasoning } = this.getModel() const methodIsAzureAiInference = this._isAzureAiInference(this.options.openAiBaseUrl) if (this.options.openAiStreamingEnabled ?? true) { @@ -359,10 +369,10 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl ], stream: true, ...(isGrokXAI ? {} : { stream_options: { include_usage: true } }), - reasoning_effort: modelInfo.reasoningEffort as "low" | "medium" | "high" | undefined, + ...(reasoning && reasoning), temperature: undefined, // Tools are always present (minimum ALWAYS_AVAILABLE_TOOLS) - tools: this.convertToolsForOpenAI(metadata?.tools), + tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false), tool_choice: metadata?.tool_choice, parallel_tool_calls: metadata?.parallelToolCalls ?? true, } @@ -393,10 +403,10 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl }, ...convertToOpenAiMessages(messages), ], - reasoning_effort: modelInfo.reasoningEffort as "low" | "medium" | "high" | undefined, + ...(reasoning && reasoning), temperature: undefined, // Tools are always present (minimum ALWAYS_AVAILABLE_TOOLS) - tools: this.convertToolsForOpenAI(metadata?.tools), + tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false), tool_choice: metadata?.tool_choice, parallel_tool_calls: metadata?.parallelToolCalls ?? true, } diff --git a/src/api/providers/opencode-go.ts b/src/api/providers/opencode-go.ts index be53dc1c02..3e490ee5ce 100644 --- a/src/api/providers/opencode-go.ts +++ b/src/api/providers/opencode-go.ts @@ -189,7 +189,7 @@ export class OpencodeGoHandler extends RouterProvider implements SingleCompletio this.options.includeMaxTokens === true ? this.options.modelMaxTokens || maxTokens : maxTokens, stream: true, stream_options: { include_usage: true }, - tools: this.convertToolsForOpenAI(metadata?.tools), + tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false), tool_choice: metadata?.tool_choice, parallel_tool_calls: metadata?.parallelToolCalls ?? true, ...(reasoningEffort && { diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 3e59b4360b..d74920adff 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -327,7 +327,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH }, }), ...(reasoning && { reasoning }), - tools: this.convertToolsForOpenAI(metadata?.tools), + tools: this.convertToolsForOpenAI(metadata?.tools, this.options.openAiToolStrictMode ?? false), tool_choice: metadata?.tool_choice, } diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index 8b11c128c7..e095954255 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -162,6 +162,16 @@ export const OpenAICompatible = ({ onChange={handleInputChange("openAiStreamingEnabled", noTransform)}> {t("settings:modelInfo.enableStreaming")} +
+ + {t("settings:modelInfo.strictToolSchemas")} + +
+ {t("settings:modelInfo.strictToolSchemasDescription")} +
+
{{serviceName}}. Si no esteu segur de quin model triar, Zoo Code funciona millor amb {{defaultModelId}}. També podeu cercar \"free\" per a opcions gratuïtes actualment disponibles.", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 5a8c05551f..9ed1bda05e 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Kostenlos bis zu {{count}} Anfragen pro Minute. Danach hängt die Abrechnung von der Prompt-Größe ab.", "pricingDetails": "Weitere Informationen finden Sie unter Preisdetails.", "billingEstimate": "* Die Abrechnung ist eine Schätzung - die genauen Kosten hängen von der Prompt-Größe ab." - } + }, + "strictToolSchemas": "Strikte Tool-Schemas", + "strictToolSchemasDescription": "Aktiviert den strikten Modus für Funktionstool-Schemas und stellt sicher, dass Tool-Ausgaben genau mit dem Schema übereinstimmen. Manche Provider unterstützen den strikten Modus möglicherweise nicht. MCP-Tools werden unabhängig von dieser Einstellung immer als nicht-strikt behandelt. Diese Einstellung wird pro Profil gespeichert und gilt auch für andere Provider, die das OpenAI-Protokoll im selben Profil verwenden." }, "modelPicker": { "automaticFetch": "Die Erweiterung ruft automatisch die neueste Liste der auf {{serviceName}} verfügbaren Modelle ab. Wenn du dir nicht sicher bist, welches Modell du wählen sollst, funktioniert Zoo Code am besten mit {{defaultModelId}}. Du kannst auch versuchen, nach \"kostenlos\" zu suchen, um die derzeit verfügbaren kostenlosen Optionen zu finden.", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 2aacc322f0..58c1c547d1 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -1044,6 +1044,8 @@ "enableR1FormatTips": "Must be enabled when using R1 models such as QWQ to prevent 400 errors", "useAzure": "Use Azure", "azureApiVersion": "Set Azure API version", + "strictToolSchemas": "Strict tool schemas", + "strictToolSchemasDescription": "Enables strict mode for function tool schemas, ensuring tool outputs match the schema exactly. Some providers may not support strict mode. MCP tools are always kept non-strict regardless of this setting. This setting is saved per profile and also applies to other providers that use the OpenAI protocol within the same profile.", "gemini": { "freeRequests": "* Free up to {{count}} requests per minute. After that, billing depends on prompt size.", "pricingDetails": "For more info, see pricing details.", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 3f99fc1b14..c630619e2f 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Gratis hasta {{count}} solicitudes por minuto. Después de eso, la facturación depende del tamaño del prompt.", "pricingDetails": "Para más información, consulte los detalles de precios.", "billingEstimate": "* La facturación es una estimación - el costo exacto depende del tamaño del prompt." - } + }, + "strictToolSchemas": "Esquemas de herramientas estrictos", + "strictToolSchemasDescription": "Activa el modo estricto para los esquemas de funciones de herramientas, asegurando que las salidas de las herramientas coincidan exactamente con el esquema. Algunos proveedores pueden no soportar el modo estricto. Las herramientas MCP siempre se mantienen no estrictas independientemente de esta configuración. Esta configuración se guarda por perfil y también se aplica a otros proveedores que utilicen el protocolo OpenAI dentro del mismo perfil." }, "modelPicker": { "automaticFetch": "La extensión obtiene automáticamente la lista más reciente de modelos disponibles en {{serviceName}}. Si no está seguro de qué modelo elegir, Zoo Code funciona mejor con {{defaultModelId}}. También puede buscar \"free\" para opciones sin costo actualmente disponibles.", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index ac0e6afb22..4aaa69ae19 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Gratuit jusqu'à {{count}} requêtes par minute. Après cela, la facturation dépend de la taille du prompt.", "pricingDetails": "Pour plus d'informations, voir les détails de tarification.", "billingEstimate": "* La facturation est une estimation - le coût exact dépend de la taille du prompt." - } + }, + "strictToolSchemas": "Schémas d'outils stricts", + "strictToolSchemasDescription": "Active le mode strict pour les schémas de fonctions d'outils, garantissant que les sorties des outils correspondent exactement au schéma. Certains fournisseurs peuvent ne pas prendre en charge le mode strict. Les outils MCP sont toujours maintenus non stricts, quelle que soit ce paramètre. Ce paramètre est sauvegardé par profil et s'applique également aux autres fournisseurs utilisant le protocole OpenAI dans le même profil." }, "modelPicker": { "automaticFetch": "L'extension récupère automatiquement la liste la plus récente des modèles disponibles sur {{serviceName}}. Si vous ne savez pas quel modèle choisir, Zoo Code fonctionne mieux avec {{defaultModelId}}. Vous pouvez également rechercher \"free\" pour les options gratuites actuellement disponibles.", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index b720a5db83..b848453fc3 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* प्रति मिनट {{count}} अनुरोधों तक मुफ्त। उसके बाद, बिलिंग प्रॉम्प्ट आकार पर निर्भर करती है।", "pricingDetails": "अधिक जानकारी के लिए, मूल्य निर्धारण विवरण देखें।", "billingEstimate": "* बिलिंग एक अनुमान है - सटीक लागत प्रॉम्प्ट आकार पर निर्भर करती है।" - } + }, + "strictToolSchemas": "सख्त टूल स्कीमा", + "strictToolSchemasDescription": "फ़ंक्शन टूल स्कीमा के लिए सख्त मोड सक्षम करता है, जिससे टूल आउटपुट स्कीमा से बिल्कुल मेल खाते हैं। कुछ प्रदाता सख्त मोड का समर्थन नहीं कर सकते। MCP टूल इस सेटिंग की परवाह किए बिना हमेशा गैर-सख्त रखे जाते हैं। यह सेटिंग प्रोफ़ाइल के अनुसार सहेजी जाती है और उन अन्य प्रदाताओं पर भी लागू होती है जो उसी प्रोफ़ाइल में OpenAI प्रोटोकॉल का उपयोग करते हैं।" }, "modelPicker": { "automaticFetch": "एक्सटेंशन {{serviceName}} पर उपलब्ध मॉडलों की नवीनतम सूची स्वचालित रूप से प्राप्त करता है। यदि आप अनिश्चित हैं कि कौन सा मॉडल चुनना है, तो Zoo Code {{defaultModelId}} के साथ सबसे अच्छा काम करता है। आप वर्तमान में उपलब्ध निःशुल्क विकल्पों के लिए \"free\" भी खोज सकते हैं।", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index c46cc5acf1..c1bd7dc518 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Gratis hingga {{count}} permintaan per menit. Setelah itu, penagihan tergantung pada ukuran prompt.", "pricingDetails": "Untuk info lebih lanjut, lihat detail harga.", "billingEstimate": "* Penagihan adalah estimasi - biaya sebenarnya tergantung pada ukuran prompt." - } + }, + "strictToolSchemas": "Skema tool yang ketat", + "strictToolSchemasDescription": "Mengaktifkan mode ketat untuk skema fungsi tool, memastikan output tool sesuai dengan skema secara tepat. Beberapa provider mungkin tidak mendukung mode ketat. Tool MCP selalu dijaga tetap tidak ketat terlepas dari pengaturan ini. Pengaturan ini disimpan per profil dan juga berlaku untuk provider lain yang menggunakan protokol OpenAI dalam profil yang sama." }, "modelPicker": { "automaticFetch": "Ekstensi secara otomatis mengambil daftar model terbaru yang tersedia di {{serviceName}}. Jika kamu tidak yakin model mana yang harus dipilih, Zoo Code bekerja terbaik dengan {{defaultModelId}}. Kamu juga dapat mencoba mencari \"free\" untuk opsi tanpa biaya yang saat ini tersedia.", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index ff00dacca7..14d3636f49 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Gratuito fino a {{count}} richieste al minuto. Dopo, la fatturazione dipende dalla dimensione del prompt.", "pricingDetails": "Per maggiori informazioni, vedi i dettagli sui prezzi.", "billingEstimate": "* La fatturazione è una stima - il costo esatto dipende dalle dimensioni del prompt." - } + }, + "strictToolSchemas": "Schema strumenti rigidi", + "strictToolSchemasDescription": "Abilita la modalità rigida per gli schema delle funzioni degli strumenti, garantendo che gli output degli strumenti corrispondano esattamente allo schema. Alcuni provider potrebbero non supportare la modalità rigida. Gli strumenti MCP vengono sempre mantenuti non rigidi indipendentemente da questa impostazione. Questa impostazione viene salvata per profilo e si applica anche ad altri provider che utilizzano il protocollo OpenAI nello stesso profilo." }, "modelPicker": { "automaticFetch": "L'estensione recupera automaticamente l'elenco più recente dei modelli disponibili su {{serviceName}}. Se non sei sicuro di quale modello scegliere, Zoo Code funziona meglio con {{defaultModelId}}. Puoi anche cercare \"free\" per opzioni gratuite attualmente disponibili.", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index cdcb377cc9..a0fd4201bd 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* 1分間あたり{{count}}リクエストまで無料。それ以降は、プロンプトサイズに応じて課金されます。", "pricingDetails": "詳細は価格情報をご覧ください。", "billingEstimate": "* 課金は見積もりです - 正確な費用はプロンプトのサイズによって異なります。" - } + }, + "strictToolSchemas": "Strict tool schemas", + "strictToolSchemasDescription": "関数ツールスキーマに対してStrictモードを有効にし、ツールの出力がスキーマに正確に一致するようにします。一部のプロバイダーはStrictモードをサポートしていない場合があります。MCPツールはこの設定に関係なく常にnon-strictに保持されます。この設定はプロファイルごとに保存され、同じプロファイル内でOpenAIプロトコルを使用する他のプロバイダーにも適用されます。" }, "modelPicker": { "automaticFetch": "拡張機能は{{serviceName}}で利用可能な最新のモデルリストを自動的に取得します。どのモデルを選ぶべきか迷っている場合、Zoo Codeは{{defaultModelId}}で最適に動作します。また、「free」で検索すると、現在利用可能な無料オプションを見つけることができます。", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 4a7845ac2a..d90e50bc54 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* 분당 {{count}}개의 요청까지 무료. 이후에는 프롬프트 크기에 따라 요금이 부과됩니다.", "pricingDetails": "자세한 내용은 가격 정보를 참조하세요.", "billingEstimate": "* 요금은 추정치입니다 - 정확한 비용은 프롬프트 크기에 따라 달라집니다." - } + }, + "strictToolSchemas": "엄격한 도구 스키마", + "strictToolSchemasDescription": "함수 도구 스키마에 대한 엄격한 모드를 활성화하여 도구 출력이 스키마와 정확히 일치하도록 합니다. 일부 프로바이더는 엄격한 모드를 지원하지 않을 수 있습니다. MCP 도구는 이 설정에 관계없이 항상 non-strict로 유지됩니다. 이 설정은 프로필별로 저장되며 동일한 프로필 내에서 OpenAI 프로토콜을 사용하는 다른 프로바이더에도 적용됩니다." }, "modelPicker": { "automaticFetch": "확장 프로그램은 {{serviceName}}에서 사용 가능한 최신 모델 목록을 자동으로 가져옵니다. 어떤 모델을 선택해야 할지 확실하지 않다면, Zoo Code는 {{defaultModelId}}로 가장 잘 작동합니다. 현재 사용 가능한 무료 옵션을 찾으려면 \"free\"를 검색해 볼 수도 있습니다.", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 768018c3ef..4db1d3a14d 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Gratis tot {{count}} verzoeken per minuut. Daarna is de prijs afhankelijk van de promptgrootte.", "pricingDetails": "Zie prijsdetails voor meer info.", "billingEstimate": "* Facturering is een schatting - de exacte kosten hangen af van de promptgrootte." - } + }, + "strictToolSchemas": "Strikte tool-schema's", + "strictToolSchemasDescription": "Schakelt de strikte modus in voor functie-tool-schema's en zorgt ervoor dat tool-uitvoer exact overeenkomt met het schema. Sommige providers ondersteunen de strikte modus mogelijk niet. MCP-tools worden altijd als niet-strikt behouden, ongeacht deze instelling. Deze instelling wordt per profiel opgeslagen en geldt ook voor andere providers die het OpenAI-protocol gebruiken binnen hetzelfde profiel." }, "modelPicker": { "automaticFetch": "De extensie haalt automatisch de nieuwste lijst met modellen op van {{serviceName}}. Weet je niet welk model je moet kiezen? Zoo Code werkt het beste met {{defaultModelId}}. Je kunt ook zoeken op 'free' voor gratis opties die nu beschikbaar zijn.", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 37b37df875..9218128758 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Darmowe do {{count}} zapytań na minutę. Po tym, rozliczanie zależy od rozmiaru podpowiedzi.", "pricingDetails": "Więcej informacji znajdziesz w szczegółach cennika.", "billingEstimate": "* Rozliczenie jest szacunkowe - dokładny koszt zależy od rozmiaru podpowiedzi." - } + }, + "strictToolSchemas": "Ścisłe schematy narzędzi", + "strictToolSchemasDescription": "Włącza tryb ścisły dla schematów funkcji narzędzi, zapewniając, że wyjścia narzędzi dokładnie odpowiadają schematowi. Niektórzy dostawcy mogą nie obsługiwać trybu ścisłego. Narzędzia MCP zawsze pozostają nieścisłe niezależnie od tego ustawienia. To ustawienie jest zapisywane dla profilu i obowiązuje również dla innych dostawców korzystających z protokołu OpenAI w tym samym profilu." }, "modelPicker": { "automaticFetch": "Rozszerzenie automatycznie pobiera najnowszą listę modeli dostępnych w {{serviceName}}. Jeśli nie jesteś pewien, który model wybrać, Zoo Code działa najlepiej z {{defaultModelId}}. Możesz również wyszukać \"free\", aby znaleźć obecnie dostępne opcje bezpłatne.", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index c3b91d6b58..21a9218610 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Gratuito até {{count}} requisições por minuto. Depois disso, a cobrança depende do tamanho do prompt.", "pricingDetails": "Para mais informações, consulte os detalhes de preços.", "billingEstimate": "* A cobrança é uma estimativa - o custo exato depende do tamanho do prompt." - } + }, + "strictToolSchemas": "Esquemas de ferramentas estritos", + "strictToolSchemasDescription": "Ativa o modo estrito para esquemas de funções de ferramentas, garantindo que as saídas das ferramentas correspondam exatamente ao esquema. Alguns provedores podem não suportar o modo estrito. Ferramentas MCP são sempre mantidas como não estritas, independente dessa configuração. Essa configuração é salva por perfil e também se aplica a outros provedores que usam o protocolo OpenAI dentro do mesmo perfil." }, "modelPicker": { "automaticFetch": "A extensão busca automaticamente a lista mais recente de modelos disponíveis em {{serviceName}}. Se você não tem certeza sobre qual modelo escolher, o Zoo Code funciona melhor com {{defaultModelId}}. Você também pode pesquisar por \"free\" para encontrar opções gratuitas atualmente disponíveis.", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index c428b31ec1..992c41645a 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Бесплатно до {{count}} запросов в минуту. Далее тарификация зависит от размера подсказки.", "pricingDetails": "Подробнее о ценах.", "billingEstimate": "* Счёт — приблизительный, точная стоимость зависит от размера подсказки." - } + }, + "strictToolSchemas": "Строгие схемы инструментов", + "strictToolSchemasDescription": "Включает строгий режим для схем функций инструментов, гарантируя, что вывод инструментов точно соответствует схеме. Некоторые провайдеры могут не поддерживать строгий режим. Инструменты MCP всегда остаются нестрогими независимо от этой настройки. Эта настройка сохраняется для каждого профиля и также применяется к другим провайдерам, использующим протокол OpenAI в том же профиле." }, "modelPicker": { "automaticFetch": "Расширение автоматически получает актуальный список моделей на {{serviceName}}. Если не уверены, что выбрать, Zoo Code лучше всего работает с {{defaultModelId}}. Также попробуйте поискать \"free\" для бесплатных вариантов.", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index bccb1c08aa..a54592e4c9 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Dakikada {{count}} isteğe kadar ücretsiz. Bundan sonra, ücretlendirme istem boyutuna bağlıdır.", "pricingDetails": "Daha fazla bilgi için fiyatlandırma ayrıntılarına bakın.", "billingEstimate": "* Ücretlendirme bir tahmindir - kesin maliyet istem boyutuna bağlıdır." - } + }, + "strictToolSchemas": "Sıkı tool şemaları", + "strictToolSchemasDescription": "Fonksiyon tool şemaları için sıkı modu etkinleştirir, tool çıktılarının şemayla tam olarak eşleşmesini sağlar. Bazı sağlayıcılar sıkı modu desteklemeyebilir. MCP tool'ları bu ayar ne olursa olsun her zaman sıkı olmayan şekilde tutulur. Bu ayar profile göre kaydedilir ve aynı profilde OpenAI protokolünü kullanan diğer sağlayıcılara da uygulanır." }, "modelPicker": { "automaticFetch": "Uzantı {{serviceName}} üzerinde bulunan mevcut modellerin en güncel listesini otomatik olarak alır. Hangi modeli seçeceğinizden emin değilseniz, Zoo Code {{defaultModelId}} ile en iyi şekilde çalışır. Şu anda mevcut olan ücretsiz seçenekleri bulmak için \"free\" araması da yapabilirsiniz.", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 6b20b9a9a9..f304e3f74e 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* Miễn phí đến {{count}} yêu cầu mỗi phút. Sau đó, thanh toán phụ thuộc vào kích thước lời nhắc.", "pricingDetails": "Để biết thêm thông tin, xem chi tiết giá.", "billingEstimate": "* Thanh toán là ước tính - chi phí chính xác phụ thuộc vào kích thước lời nhắc." - } + }, + "strictToolSchemas": "Schema công cụ nghiêm ngặt", + "strictToolSchemasDescription": "Bật chế độ nghiêm ngặt cho schema hàm công cụ, đảm bảo đầu ra của công cụ khớp chính xác với schema. Một số nhà cung cấp có thể không hỗ trợ chế độ nghiêm ngặt. Công cụ MCP luôn được giữ ở chế độ không nghiêm ngặt bất kể thiết lập này. Thiết lập này được lưu theo hồ sơ và cũng áp dụng cho các nhà cung cấp khác sử dụng giao thức OpenAI trong cùng hồ sơ." }, "modelPicker": { "automaticFetch": "Tiện ích mở rộng tự động lấy danh sách mới nhất các mô hình có sẵn trên {{serviceName}}. Nếu bạn không chắc chắn nên chọn mô hình nào, Zoo Code hoạt động tốt nhất với {{defaultModelId}}. Bạn cũng có thể thử tìm kiếm \"free\" cho các tùy chọn miễn phí hiện có.", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index c206c26108..fd098ae945 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -968,7 +968,9 @@ "freeRequests": "* 每分钟免费 {{count}} 个请求。之后,计费取决于提示大小。", "pricingDetails": "有关更多信息,请参阅定价详情。", "billingEstimate": "* 计费为估计值 - 具体费用取决于提示大小。" - } + }, + "strictToolSchemas": "严格工具 Schema", + "strictToolSchemasDescription": "为函数工具 Schema 启用严格模式,确保工具输出与 Schema 完全匹配。部分 Provider 可能不支持严格模式。MCP 工具无论此设置如何始终保持非严格状态。此设置按 Profile 保存,同时也适用于同一 Profile 中使用 OpenAI 协议的其他 Provider。" }, "modelPicker": { "automaticFetch": "自动获取 {{serviceName}} 上可用的最新模型列表。如果您不确定选择哪个模型,Zoo Code 与 {{defaultModelId}} 配合最佳。您还可以搜索\"free\"以查找当前可用的免费选项。", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 64eb5e0b29..ad6426ec29 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -995,7 +995,9 @@ "freeRequests": "* 每分鐘可免費使用 {{count}} 次請求,超過後將依提示詞大小計費。", "pricingDetails": "詳細資訊請參閱定價說明。", "billingEstimate": "* 費用為估算值 - 實際費用取決於提示大小。" - } + }, + "strictToolSchemas": "嚴格工具 Schema", + "strictToolSchemasDescription": "為函式工具 Schema 啟用嚴格模式,確保工具輸出與 Schema 完全匹配。部分 Provider 可能不支援嚴格模式。MCP 工具無論此設定如何始終保持非嚴格狀態。此設定依 Profile 儲存,同時也適用於同一 Profile 中使用 OpenAI 協定的其他 Provider。" }, "modelPicker": { "automaticFetch": "此擴充功能會自動從 {{serviceName}} 取得最新的可用模型清單。如果不確定要選哪個模型,建議使用 {{defaultModelId}},這是與 Zoo Code 最佳搭配的模型。您也可以搜尋「free」來檢視目前可用的免費選項。",