From 7cc20fcb6ada9b2e3d68f42dec48aa70da8db85c Mon Sep 17 00:00:00 2001 From: Kinso <529724975@qq.com> Date: Wed, 26 Aug 2026 23:51:28 +0800 Subject: [PATCH 1/2] feat(core): support glm-5.3-flash under glm-v family (#2) GLM-5.3-Flash is an always-thinking vision model: it rejects thinking.type=disabled with error 1210, so the glm-v adapter now branches on the resolved model name. For glm-5.3-flash it keeps thinking enabled (clear_thinking=false), steers depth via reasoning_effort (low when the user disables reasoning), and applies the officially recommended temperature=1 / top_p=0.95 defaults. Other glm-v models keep the toggleable-thinking contract unchanged. Field-tested against the Zhipu Coding Plan endpoint in KSL-49: same-sample accuracy on par with the production vision models (qwen3.8-max / MiniMax-M3), locate IoU 0.698 with the glm-v 0-1000 bbox contract. The model catalog docs (en/zh) list glm-5.3-flash under glm-v with the always-thinking note. Co-authored-by: jzli Co-authored-by: multica-agent --- apps/site/docs/en/model-common-config.mdx | 1 + apps/site/docs/zh/model-common-config.mdx | 1 + .../core/src/ai-model/model-adapter/types.ts | 10 ++ packages/core/src/ai-model/models/glm.ts | 39 +++++++- .../core/src/ai-model/service-caller/index.ts | 1 + .../tests/unit-test/model-adapter/glm.test.ts | 91 +++++++++++++++++++ 6 files changed, 140 insertions(+), 3 deletions(-) diff --git a/apps/site/docs/en/model-common-config.mdx b/apps/site/docs/en/model-common-config.mdx index b6638e1405..7c5102e755 100644 --- a/apps/site/docs/en/model-common-config.mdx +++ b/apps/site/docs/en/model-common-config.mdx @@ -400,6 +400,7 @@ MIDSCENE_INSIGHT_MODEL_FAMILY="xiaomi-mimo" | Model version | Commonly used model names | `MIDSCENE_MODEL_FAMILY` | Notes | | --- | --- | --- | --- | +| GLM-5.3 series | `glm-5.3-flash` | `glm-v` | Always-thinking: `thinking.type` cannot be disabled, so disabling reasoning sends `reasoning_effort: low` instead. | | GLM-5V series | `glm-5v-turbo` | `glm-v` | — | | GLM-4.6 series | `glm-4.6v` | `glm-v` | `glm-4.6v` is open-source. | diff --git a/apps/site/docs/zh/model-common-config.mdx b/apps/site/docs/zh/model-common-config.mdx index d28bbf412b..33c96d2696 100644 --- a/apps/site/docs/zh/model-common-config.mdx +++ b/apps/site/docs/zh/model-common-config.mdx @@ -401,6 +401,7 @@ MIDSCENE_INSIGHT_MODEL_FAMILY="xiaomi-mimo" | 模型版本 | 常用模型名称 | `MIDSCENE_MODEL_FAMILY` | 备注 | | --- | --- | --- | --- | +| GLM-5.3 系列 | `glm-5.3-flash` | `glm-v` | 始终思考:不支持关闭 `thinking.type`,关闭推理时会改发 `reasoning_effort: low`。 | | GLM-5V 系列 | `glm-5v-turbo` | `glm-v` | — | | GLM-4.6 系列 | `glm-4.6v` | `glm-v` | `glm-4.6v` 是开源模型。 | diff --git a/packages/core/src/ai-model/model-adapter/types.ts b/packages/core/src/ai-model/model-adapter/types.ts index 65b5b9a87f..21ea0d1232 100644 --- a/packages/core/src/ai-model/model-adapter/types.ts +++ b/packages/core/src/ai-model/model-adapter/types.ts @@ -63,6 +63,12 @@ export type ChatCompletionUnsupportedUserConfig = export interface ChatCompletionCallInput { intent?: TIntent; + /** + * Model name resolved for this call. Adapters may use it to branch within a + * family when a specific model requires different request parameters (for + * example, always-thinking variants). + */ + modelName?: string; userConfig?: ChatCompletionCallUserConfig; /** * Number of preceding semantic parsing failures for this request. @@ -83,6 +89,10 @@ export interface ChatCompletionCallInput { export interface ChatCompletionCallContext { intent?: TIntent; + /** + * Model name resolved for this call; see ChatCompletionCallInput. + */ + modelName?: string; userConfig: ChatCompletionCallUserConfig; semanticRetryAttempt?: number; requiresOriginalImageDetail?: boolean; diff --git a/packages/core/src/ai-model/models/glm.ts b/packages/core/src/ai-model/models/glm.ts index 0736b8dec0..af2498d6c5 100644 --- a/packages/core/src/ai-model/models/glm.ts +++ b/packages/core/src/ai-model/models/glm.ts @@ -5,14 +5,31 @@ import type { ModelAdapterDefinition, } from '../model-adapter/types'; +const ALWAYS_THINKING_GLM_MODEL_PATTERN = /^glm-5\.3-flash\b/; + const buildGlmChatCompletionParams = ( input: ChatCompletionCallContext, ): ChatCompletionParamsResult => { - const { midsceneDefaults, userConfig } = input; + const { midsceneDefaults, userConfig, modelName } = input; const { reasoningEnabled } = userConfig; const commonOverrideConfig: Record = {}; - if (userConfig.temperature !== undefined) { + // GLM models that cannot turn thinking off. GLM-5.3-Flash rejects + // `thinking.type: 'disabled'` with error 1210 ("该模型始终思考,不支持关闭 + // 思考"), so for these models the adapter keeps thinking enabled and steers + // its depth with `reasoning_effort` instead. Field-tested against the + // Zhipu Coding Plan endpoint in KSL-49 (2026-08-26). + const alwaysThinking = ALWAYS_THINKING_GLM_MODEL_PATTERN.test( + modelName ?? '', + ); + + if (alwaysThinking) { + // Officially recommended sampling parameters for GLM-5.3-Flash + // (https://docs.bigmodel.cn/cn/guide/models/vlm/glm-5.3-flash), and the + // values its vision integration was field-tested with. + commonOverrideConfig.temperature = userConfig.temperature ?? 1; + commonOverrideConfig.top_p = 0.95; + } else if (userConfig.temperature !== undefined) { commonOverrideConfig.temperature = userConfig.temperature; } @@ -27,7 +44,21 @@ const buildGlmChatCompletionParams = ( const modelSpecificConfig: Record = {}; - if (reasoningEnabled !== 'default') { + if (alwaysThinking) { + if (reasoningEnabled !== 'default') { + modelSpecificConfig.thinking = { + type: 'enabled', + clear_thinking: false, + }; + // Thinking cannot be disabled, so a "no reasoning" intent maps to the + // cheapest effort level. `low` was sufficient for the KSL-49 vision + // probes; users can raise it via the reasoning-effort config. + modelSpecificConfig.reasoning_effort = + userConfig.reasoningEffort ?? 'low'; + } else if (userConfig.reasoningEffort) { + modelSpecificConfig.reasoning_effort = userConfig.reasoningEffort; + } + } else if (reasoningEnabled !== 'default') { modelSpecificConfig.thinking = { type: (reasoningEnabled ?? false) ? 'enabled' : 'disabled', }; @@ -45,6 +76,8 @@ const buildGlmChatCompletionParams = ( export const glmAdapters = { 'glm-v': { chatCompletion: { + // reasoningEffort is honored only by the always-thinking GLM models + // (see above); reasoningBudget is not supported by the family at all. unsupportedUserConfig: ['reasoningEffort', 'reasoningBudget'], buildChatCompletionParams: buildGlmChatCompletionParams, useReasoningAsContentFallback: true, diff --git a/packages/core/src/ai-model/service-caller/index.ts b/packages/core/src/ai-model/service-caller/index.ts index 162909f568..8ff2f8040f 100644 --- a/packages/core/src/ai-model/service-caller/index.ts +++ b/packages/core/src/ai-model/service-caller/index.ts @@ -403,6 +403,7 @@ export async function callAI( : undefined; const chatCompletionInput = { intent: modelConfig.intent, + modelName: modelConfig.modelName, userConfig: { temperature: modelConfig.temperature, reasoningEnabled: modelConfig.reasoningEnabled, diff --git a/packages/core/tests/unit-test/model-adapter/glm.test.ts b/packages/core/tests/unit-test/model-adapter/glm.test.ts index ef07b3d888..65a74f74fd 100644 --- a/packages/core/tests/unit-test/model-adapter/glm.test.ts +++ b/packages/core/tests/unit-test/model-adapter/glm.test.ts @@ -110,3 +110,94 @@ describe('glm model adapter', () => { expect(result.config.response_format).toBeUndefined(); }); }); + +describe('glm-5.3-flash always-thinking contract', () => { + it('never sends thinking.type=disabled for glm-5.3-flash', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + userConfig: {}, + }); + expect(result.config).toEqual({ + temperature: 1, + top_p: 0.95, + thinking: { type: 'enabled', clear_thinking: false }, + reasoning_effort: 'low', + }); + }); + + it('maps a reasoning-disable intent to effort=low for glm-5.3-flash', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + userConfig: { reasoningEnabled: false }, + }); + expect(result.config).toEqual({ + temperature: 1, + top_p: 0.95, + thinking: { type: 'enabled', clear_thinking: false }, + reasoning_effort: 'low', + }); + }); + + it('forwards an explicit reasoning effort for glm-5.3-flash', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + userConfig: { reasoningEnabled: true, reasoningEffort: 'high' }, + }); + expect(result.config).toEqual({ + temperature: 1, + top_p: 0.95, + thinking: { type: 'enabled', clear_thinking: false }, + reasoning_effort: 'high', + }); + }); + + it('follows provider default for glm-5.3-flash when reasoningEnabled=default', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + userConfig: { reasoningEnabled: 'default' }, + }); + expect(result.config).toEqual({ + temperature: 1, + top_p: 0.95, + }); + }); + + it('keeps an explicit user temperature for glm-5.3-flash', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + userConfig: { temperature: 0 }, + }); + expect(result.config).toEqual({ + temperature: 0, + top_p: 0.95, + thinking: { type: 'enabled', clear_thinking: false }, + reasoning_effort: 'low', + }); + }); + + it('combines json_object response format with always-thinking params', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + expectedJsonObjectResponse: true, + userConfig: {}, + }); + expect(result.config).toEqual({ + temperature: 1, + top_p: 0.95, + response_format: { type: 'json_object' }, + thinking: { type: 'enabled', clear_thinking: false }, + reasoning_effort: 'low', + }); + }); + + it('keeps toggleable thinking for non-flash glm-v models', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5v-turbo', + userConfig: {}, + }); + expect(result.config).toEqual({ + temperature: 0, + thinking: { type: 'disabled' }, + }); + }); +}); From 19be45cbacc589829a605217831a64bb0fa6cc04 Mon Sep 17 00:00:00 2001 From: jzli Date: Mon, 31 Aug 2026 19:04:24 +0800 Subject: [PATCH 2/2] fix(core): align glm-5.3-flash effort handling with the reasoning contract Address review feedback on #3071: - reasoningEnabled=default no longer forwards reasoning_effort; the default mode follows provider behavior and ignores explicit effort settings, as documented in model-config.mdx. - A 'low' effort is now applied only for the no-reasoning intent (explicit false or unset); explicitly enabling reasoning without an effort keeps the provider default instead of silently minimizing reasoning. - List Zhipu GLM (glm-5.3-flash) in the MIDSCENE_MODEL_REASONING_EFFORT supported families in both en/zh model-config.mdx. - Cover both behavior changes with unit tests. --- apps/site/docs/en/model-config.mdx | 1 + apps/site/docs/zh/model-config.mdx | 1 + packages/core/src/ai-model/models/glm.ts | 20 ++++++++++------ .../tests/unit-test/model-adapter/glm.test.ts | 24 +++++++++++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/apps/site/docs/en/model-config.mdx b/apps/site/docs/en/model-config.mdx index 33f43c8054..f78563043e 100644 --- a/apps/site/docs/en/model-config.mdx +++ b/apps/site/docs/en/model-config.mdx @@ -107,6 +107,7 @@ The following model families currently support `MIDSCENE_MODEL_REASONING_ENABLED - Gemini: Maps to `thinking_config.thinking_level`. - GPT-5: Maps to `reasoning_effort`. - Kimi K3 series: Maps to `reasoning_effort`. +- Zhipu GLM: Maps to `reasoning_effort` (glm-5.3-flash only). Provider support and accepted values vary. See each provider's official documentation for supported model versions and values. If the current model does not support an explicit setting, Midscene ignores it instead of guessing a provider-specific private parameter. diff --git a/apps/site/docs/zh/model-config.mdx b/apps/site/docs/zh/model-config.mdx index 7fb3c20d8d..95441c84aa 100644 --- a/apps/site/docs/zh/model-config.mdx +++ b/apps/site/docs/zh/model-config.mdx @@ -106,6 +106,7 @@ Midscene 默认关闭模型原生思考,以获得更好的执行速度和稳 - Gemini:对应 `thinking_config.thinking_level`。 - GPT-5:对应 `reasoning_effort`。 - Kimi K3 系列:对应 `reasoning_effort`。 +- 智谱 GLM:对应 `reasoning_effort`(仅 glm-5.3-flash)。 不同模型服务商支持的参数和值不同。具体取值和适用模型版本请参考对应服务商的官方文档。如果当前模型不支持某项显式配置,Midscene 会忽略该配置,不会猜测服务商的私有参数。 diff --git a/packages/core/src/ai-model/models/glm.ts b/packages/core/src/ai-model/models/glm.ts index af2498d6c5..52190d684e 100644 --- a/packages/core/src/ai-model/models/glm.ts +++ b/packages/core/src/ai-model/models/glm.ts @@ -50,14 +50,20 @@ const buildGlmChatCompletionParams = ( type: 'enabled', clear_thinking: false, }; - // Thinking cannot be disabled, so a "no reasoning" intent maps to the - // cheapest effort level. `low` was sufficient for the KSL-49 vision - // probes; users can raise it via the reasoning-effort config. - modelSpecificConfig.reasoning_effort = - userConfig.reasoningEffort ?? 'low'; - } else if (userConfig.reasoningEffort) { - modelSpecificConfig.reasoning_effort = userConfig.reasoningEffort; + if (reasoningEnabled !== true) { + // Thinking cannot be disabled, so a "no reasoning" intent (explicit + // false or unset) maps to the cheapest effort level. `low` was + // sufficient for the KSL-49 vision probes; users can raise it via + // the reasoning-effort config. + modelSpecificConfig.reasoning_effort = + userConfig.reasoningEffort ?? 'low'; + } else if (userConfig.reasoningEffort) { + modelSpecificConfig.reasoning_effort = userConfig.reasoningEffort; + } } + // reasoningEnabled='default' sends no thinking or effort override, per + // the documented contract in docs/model-config.mdx: default follows the + // provider behavior and ignores explicit effort settings. } else if (reasoningEnabled !== 'default') { modelSpecificConfig.thinking = { type: (reasoningEnabled ?? false) ? 'enabled' : 'disabled', diff --git a/packages/core/tests/unit-test/model-adapter/glm.test.ts b/packages/core/tests/unit-test/model-adapter/glm.test.ts index 65a74f74fd..5eb08c7d75 100644 --- a/packages/core/tests/unit-test/model-adapter/glm.test.ts +++ b/packages/core/tests/unit-test/model-adapter/glm.test.ts @@ -151,6 +151,30 @@ describe('glm-5.3-flash always-thinking contract', () => { }); }); + it('keeps the provider effort default when reasoning is explicitly enabled without effort', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + userConfig: { reasoningEnabled: true }, + }); + expect(result.config).toEqual({ + temperature: 1, + top_p: 0.95, + thinking: { type: 'enabled', clear_thinking: false }, + }); + expect(result.config.reasoning_effort).toBeUndefined(); + }); + + it('ignores an explicit effort in default reasoning mode for glm-5.3-flash', () => { + const result = glmAdapter.chatCompletion.buildChatCompletionParams({ + modelName: 'glm-5.3-flash', + userConfig: { reasoningEnabled: 'default', reasoningEffort: 'high' }, + }); + expect(result.config).toEqual({ + temperature: 1, + top_p: 0.95, + }); + }); + it('follows provider default for glm-5.3-flash when reasoningEnabled=default', () => { const result = glmAdapter.chatCompletion.buildChatCompletionParams({ modelName: 'glm-5.3-flash',