diff --git a/packages/core/src/v1/config/config.ts b/packages/core/src/v1/config/config.ts index 7ebb4b69b023..e8379788772c 100644 --- a/packages/core/src/v1/config/config.ts +++ b/packages/core/src/v1/config/config.ts @@ -182,6 +182,10 @@ export const Info = Schema.Struct({ mcp_timeout: Schema.optional(PositiveInt).annotate({ description: "Timeout in milliseconds for model context protocol (MCP) requests", }), + sampling_defaults: Schema.optional(Schema.Boolean).annotate({ + description: + "Apply built-in sampling defaults (temperature, top_p, top_k) for known models. Enabled by default; set to false to leave sampling to provider defaults. Explicit per-agent values and chat.params plugins always apply.", + }), policies: Schema.optional(Schema.mutable(Schema.Array(ConfigExperimental.Policy))).annotate({ description: "Policy statements applied to supported resources, such as provider access", }), diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index fdd03d520566..e9752be57e41 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -528,6 +528,9 @@ const GEMINI_MODELS_WITH_SAMPLING_DEFAULTS = [ export function temperature(model: Provider.Model) { const id = model.api.id.toLowerCase() if (id.includes("north-mini-code")) return 1.0 + if (id.includes("qwen3-coder-next") || id.includes("qwen3.8") || id.includes("qwen-3.8")) return 1.0 + if (id.includes("qwen3") || id.includes("qwen-3")) return 0.6 + if (id.includes("qwen2.5-coder")) return 0.7 if (id.includes("qwen")) return 0.55 if (id.includes("claude")) return undefined if (id.includes("gemini")) @@ -547,6 +550,8 @@ export function temperature(model: Provider.Model) { export function topP(model: Provider.Model) { const id = model.api.id.toLowerCase() + if (id.includes("qwen3") || id.includes("qwen-3")) return 0.95 + if (id.includes("qwen2.5-coder")) return 0.8 if (id.includes("qwen")) return 1 if (id.includes("gemini")) return GEMINI_MODELS_WITH_SAMPLING_DEFAULTS.some((model) => model.test(id)) ? 0.95 : undefined diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index a99f8acff20c..121a27e39674 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -109,6 +109,7 @@ const live: Layer.Layer< auth: info, plugin, flags, + samplingDefaults: cfg.experimental?.sampling_defaults, isWorkflow, }) diff --git a/packages/opencode/src/session/llm/request.ts b/packages/opencode/src/session/llm/request.ts index 4f93411107df..e96a38c28a67 100644 --- a/packages/opencode/src/session/llm/request.ts +++ b/packages/opencode/src/session/llm/request.ts @@ -32,6 +32,7 @@ type PrepareInput = { readonly auth: Auth.Info | undefined readonly plugin: Plugin.Interface readonly flags: RuntimeFlags.Info + readonly samplingDefaults?: boolean readonly isWorkflow: boolean } @@ -111,6 +112,18 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre ...input.messages, ] + const auto = (input.samplingDefaults ?? true) + ? { + temperature: ProviderTransform.temperature(input.model), + topP: ProviderTransform.topP(input.model), + topK: ProviderTransform.topK(input.model), + } + : { + temperature: undefined, + topP: undefined, + topK: undefined, + } + const params = yield* input.plugin.trigger( "chat.params", { @@ -121,11 +134,9 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre message: input.user, }, { - temperature: input.model.capabilities.temperature - ? (input.agent.temperature ?? ProviderTransform.temperature(input.model)) - : undefined, - topP: input.agent.topP ?? ProviderTransform.topP(input.model), - topK: ProviderTransform.topK(input.model), + temperature: input.model.capabilities.temperature ? (input.agent.temperature ?? auto.temperature) : undefined, + topP: input.agent.topP ?? auto.topP, + topK: auto.topK, maxOutputTokens: ProviderTransform.maxOutputTokens(input.model, input.flags.outputTokenMax), options, }, diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 701658987402..df3ab13f9419 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3215,6 +3215,51 @@ describe("ProviderTransform.temperature - Cohere North", () => { }) }) +describe("ProviderTransform sampling defaults - Qwen", () => { + const model = (id: string) => + ({ + id: `alibaba/${id}`, + api: { id }, + }) as any + + test.each([ + "qwen3-coder-next", + "qwen3.8", + "qwen3.8-max", + ])("uses 0.95 top_p and 1.0 temperature for %s", (id) => { + expect(ProviderTransform.topP(model(id))).toBe(0.95) + expect(ProviderTransform.temperature(model(id))).toBe(1.0) + }) + + test.each([ + "qwen3", + "qwen-3", + "qwen3-max", + "qwen3.5-plus", + "qwen3.6-plus", + "qwen3.7-max", + ])("uses 0.95 top_p and 0.6 temperature for %s", (id) => { + expect(ProviderTransform.topP(model(id))).toBe(0.95) + expect(ProviderTransform.temperature(model(id))).toBe(0.6) + }) + + test.each([ + "qwen2.5-coder", + "qwen2.5-coder-32b-instruct", + ])("uses 0.8 top_p and 0.7 temperature for %s", (id) => { + expect(ProviderTransform.topP(model(id))).toBe(0.8) + expect(ProviderTransform.temperature(model(id))).toBe(0.7) + }) + + test.each([ + "qwen-plus", + "qwen-max", + ])("keeps 1 top_p and 0.55 temperature for %s", (id) => { + expect(ProviderTransform.topP(model(id))).toBe(1) + expect(ProviderTransform.temperature(model(id))).toBe(0.55) + }) +}) + describe("ProviderTransform sampling defaults - Gemini", () => { const model = (id: string) => ({ diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 90c91e9158cc..8fda9f84cce4 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -2025,6 +2025,7 @@ export type Config = { primary_tools?: Array continue_loop_on_deny?: boolean mcp_timeout?: number + sampling_defaults?: boolean policies?: Array } } diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index afe14bb604cc..556e1a21d32d 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -21543,6 +21543,9 @@ "type": "integer", "exclusiveMinimum": 0 }, + "sampling_defaults": { + "type": "boolean" + }, "policies": { "type": "array", "items": {