feat(models): add GLM-5.3-Flash thinking workaround (#5074)

This commit is contained in:
Nan Gao 2026-08-28 22:24:10 +08:00 committed by GitHub
parent 23d8e4b3a3
commit bf3e792a6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 160 additions and 0 deletions

View File

@ -186,6 +186,8 @@ That prompt is intended for coding agents. It tells the agent to clone the repo
To route OpenAI models through `/v1/responses`, keep using `langchain_openai:ChatOpenAI` and set `use_responses_api: true` with `output_version: responses/v1`.
The setup wizard includes a Z.AI GLM-5.3-Flash profile. Because that model requires thinking and only accepts its own restricted effort levels, the compatibility profile keeps thinking enabled for every foreground and background call and temporarily suppresses DeerFlow's generic effort selector. See `config.example.yaml` for the equivalent manual configuration.
For vLLM 0.19.0, use `deerflow.models.vllm_provider:VllmChatModel`. For Qwen-style reasoning models, DeerFlow toggles reasoning with `extra_body.chat_template_kwargs.enable_thinking` and preserves vLLM's non-standard `reasoning` field across multi-turn tool-call conversations. Legacy `thinking` configs are normalized automatically for backward compatibility. If the endpoint reports a cumulative usage snapshot on every streaming chunk, set `cumulative_stream_usage: true` so DeerFlow converts those snapshots into per-chunk deltas; the option is disabled by default and leaves usage unchanged when a stable completion id is unavailable. Reasoning models may also require the server to be started with `--reasoning-parser ...`. If your local vLLM deployment accepts any non-empty API key, you can still set `VLLM_API_KEY` to a placeholder value.
CLI-backed provider examples:

View File

@ -172,6 +172,8 @@ DeerFlow 新近集成了 BytePlus 自研的智能搜索与抓取工具集——[
如果要让 OpenAI 模型走 `/v1/responses`,继续使用 `langchain_openai:ChatOpenAI`,并设置 `use_responses_api: true``output_version: responses/v1`
Setup Wizard 已内置 Z.AI GLM-5.3-Flash 配置。由于该模型强制开启 thinking且只接受自身限定的 effort 档位,当前兼容配置会在前台和后台调用中始终保持 thinking 开启,并暂时屏蔽 DeerFlow 的通用 effort 选择器。等价的手动配置见 `config.example.yaml`
对于 vLLM 0.19.0,请使用 `deerflow.models.vllm_provider:VllmChatModel`。对于 Qwen 风格的推理模型DeerFlow 通过 `extra_body.chat_template_kwargs.enable_thinking` 开关推理,并在多轮 tool-call 对话中保留 vLLM 非标准的 `reasoning` 字段。旧版 `thinking` 配置会自动规范化以保持向后兼容。推理模型可能还需要在启动 vLLM 服务时加上 `--reasoning-parser ...` 参数。如果你的本地 vLLM 部署接受任意非空 API key可以把 `VLLM_API_KEY` 设为一个占位值。
CLI-backed provider 配置示例:

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import pytest
from langchain.chat_models import BaseChatModel
from langchain_core.messages import HumanMessage
from deerflow.config.app_config import AppConfig
from deerflow.config.model_config import ModelConfig
@ -297,6 +298,49 @@ def test_thinking_disabled_no_when_thinking_enabled_does_nothing(monkeypatch):
assert captured.get("reasoning_effort") is None
def test_required_thinking_profile_keeps_base_payload_when_runtime_requests_disabled():
"""Always-thinking models keep their base payload on every call.
Required-thinking models such as GLM-5.3-Flash intentionally declare no
conditional thinking settings. A runtime ``thinking_enabled=False`` must
therefore leave the profile's unconditional ``extra_body.thinking`` block
untouched, while the capability guard drops DeerFlow's generic effort value.
"""
model = ModelConfig(
name="glm-5.3-flash",
display_name="GLM-5.3-Flash",
description=None,
use="deerflow.models.patched_deepseek:PatchedChatDeepSeek",
model="glm-5.3-flash",
api_base="https://api.z.ai/api/paas/v4",
api_key="test-key",
supports_thinking=True,
supports_reasoning_effort=False,
supports_vision=True,
stream_usage=False,
extra_body={
"thinking": {"type": "enabled", "clear_thinking": True},
"tool_stream": True,
},
)
cfg = _make_app_config([model])
chat_model = factory_module.create_chat_model(
name="glm-5.3-flash",
thinking_enabled=False,
reasoning_effort="medium",
app_config=cfg,
attach_tracing=False,
)
payload = chat_model._get_request_payload([HumanMessage(content="ping")])
assert payload["extra_body"] == {
"thinking": {"type": "enabled", "clear_thinking": True},
"tool_stream": True,
}
assert "reasoning_effort" not in payload
assert chat_model.stream_usage is False
# ---------------------------------------------------------------------------
# when_thinking_disabled config
# ---------------------------------------------------------------------------

View File

@ -29,6 +29,7 @@ class TestProviders:
expected = {
"volcengine",
"zai",
"openai",
"openai_responses",
"ollama_qwen",
@ -56,6 +57,42 @@ class TestProviders:
assert providers["deepseek"].use == "deerflow.models.patched_deepseek:PatchedChatDeepSeek"
assert providers["volcengine"].extra_config["api_base"] == "https://ark.cn-beijing.volces.com/api/v3"
def test_zai_glm_flash_uses_required_thinking_workaround(self):
provider = next(p for p in LLM_PROVIDERS if p.name == "zai")
config = provider.extra_config_for("glm-5.3-flash")
assert provider.use == "deerflow.models.patched_deepseek:PatchedChatDeepSeek"
assert provider.env_var == "ZAI_API_KEY"
assert config["api_base"] == "https://api.z.ai/api/paas/v4"
assert config["supports_thinking"] is True
assert config["supports_reasoning_effort"] is False
assert "when_thinking_enabled" not in config
assert "when_thinking_disabled" not in config
assert config["extra_body"] == {
"thinking": {"type": "enabled", "clear_thinking": True},
"tool_stream": True,
}
def test_zai_glm_flash_workaround_is_preserved_in_generated_config(self):
provider = next(p for p in LLM_PROVIDERS if p.name == "zai")
content = build_minimal_config(
provider_use=provider.use,
model_name=provider.default_model,
display_name=provider.display_name,
api_key_field=provider.api_key_field,
env_var=provider.env_var,
extra_model_config=provider.extra_config,
)
model = yaml.safe_load(content)["models"][0]
assert model["model"] == "glm-5.3-flash"
assert model["api_key"] == "$ZAI_API_KEY"
assert model["supports_reasoning_effort"] is False
assert model["extra_body"]["thinking"] == {
"type": "enabled",
"clear_thinking": True,
}
def test_minimax_vision_is_per_model(self):
"""M3 supports vision; M2.7 variants are text-only.

View File

@ -184,6 +184,40 @@ models:
# extra_body:
# thinking:
# type: disabled
# Example: Z.AI GLM-5.3-Flash (required-thinking workaround)
#
# GLM-5.3-Flash cannot disable thinking and only accepts low/high/max effort.
# DeerFlow's current generic UI may request disabled thinking or emit
# minimal/medium, so this profile keeps thinking enabled unconditionally and
# suppresses generic effort forwarding. Keep the thinking block in the base
# extra_body: moving it to when_thinking_enabled would let background calls
# synthesize the invalid thinking.type=disabled + reasoning_effort=minimal pair.
# clear_thinking=true is intentional for this compatibility workaround: it
# avoids requiring exact historical reasoning replay after summarization.
#
# - name: glm-5.3-flash
# display_name: GLM-5.3-Flash
# use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
# model: glm-5.3-flash
# api_base: https://api.z.ai/api/paas/v4
# api_key: $ZAI_API_KEY
# timeout: 600.0
# max_retries: 2
# temperature: 1.0
# top_p: 0.95
# max_tokens: 131072
# context_window: 1000000
# supports_thinking: true
# supports_reasoning_effort: false
# supports_vision: true
# stream_usage: false
# extra_body:
# thinking:
# type: enabled
# clear_thinking: true
# tool_stream: true
# Example: OpenAI model
# - name: gpt-4
# display_name: GPT-4

View File

@ -175,6 +175,47 @@ LLM_PROVIDERS: list[LLMProvider] = [
"kimi-k2.7-code": False,
},
),
LLMProvider(
name="zai",
display_name="Z.AI GLM-5.3-Flash",
description="GLM-5.3-Flash with required thinking and native vision",
use="deerflow.models.patched_deepseek:PatchedChatDeepSeek",
models=["glm-5.3-flash"],
default_model="glm-5.3-flash",
env_var="ZAI_API_KEY",
package="langchain-deepseek",
extra_config={
"api_base": "https://api.z.ai/api/paas/v4",
"timeout": 600.0,
"max_retries": 2,
"temperature": 1.0,
"top_p": 0.95,
"max_tokens": 131072,
"context_window": 1000000,
"supports_thinking": True,
# GLM-5.3-Flash only accepts low/high/max, while DeerFlow's current
# generic UI can emit minimal/medium. Keep provider effort control
# disabled until model-specific reasoning capabilities are exposed.
"supports_reasoning_effort": False,
"supports_vision": True,
# The model cannot disable thinking. This unconditional base payload
# deliberately avoids when_thinking_enabled/disabled so background
# callers that request thinking_enabled=False cannot synthesize an
# invalid disabled/minimal combination in the model factory.
"extra_body": {
"thinking": {
"type": "enabled",
# Avoid preserved-thinking history requirements until the
# reasoning-history abstraction handles summarization.
"clear_thinking": True,
},
"tool_stream": True,
},
# Z.AI streams terminal usage without requiring OpenAI's undocumented
# stream_options.include_usage request field.
"stream_usage": False,
},
),
LLMProvider(
name="openai",
display_name="OpenAI",