mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* fix(models): deep-merge when_thinking_enabled on the legacy path The legacy thinking path (profiles without a `reasoning:` block, i.e. every existing config.yaml) applied `when_thinking_enabled` with a shallow `dict.update`, so a template such as `extra_body.thinking.type=enabled` replaced the profile's whole `extra_body` and silently dropped sibling keys like GLM's `extra_body.tool_stream` — but only while thinking was on. The disable branch of the same function already deep-merged into `extra_body`, and the contract path added in #5780 deep-merges through `_merge_settings` for exactly this reason (its docstring names `tool_stream`). The on/off asymmetry cannot be intended: the same profile sent `tool_stream` with thinking off and lost it with thinking on. Route the enable template through `_merge_settings` as well. Precedence is unchanged (template values win on conflicts); the only observable difference is that keys present only in the profile now survive a thinking-on request. Everything else on the legacy path — the synthesized `reasoning_effort=minimal`, the disable payloads, the supports_thinking guard — is untouched. Tests pin both directions for a base extra_body beside a thinking template, recursive merging of nested chat_template_kwargs, and template precedence. * docs(changelog): reference #5894 in the when_thinking_enabled entries * fix(models): keep the template's vLLM switch spelling authoritative when merging Review follow-up. Deep-merging the thinking template into a profile keeps both spellings of vLLM's toggle when they differ — base `chat_template_kwargs.enable_thinking: false` plus a template using the legacy `thinking: true` alias — and VllmChatModel's normalizer maps the alias onto `enable_thinking` only when the latter is absent, so the profile's `false` won and thinking stayed off despite thinking_enabled=True. The previous shallow replacement never hit this because it discarded the base switch. The contract path had the same trap since it already deep-merged, and both disable branches had the mirror image (a base `enable_thinking: true` outlived a template `thinking: false`). Route every thinking on/off merge through _merge_thinking_payload: it drops the profile's *other* spelling of the switch before the deep merge, so the payload's spelling decides in both directions on both paths, and unrelated chat_template_kwargs still survive. Tests cover both spelling combinations, both directions, and both paths, asserting the switch as the provider will send it after normalization. * fix(models): deep-merge the legacy disable template and mirror the vLLM switch instead of pruning Review of the previous head found two regressions. The legacy when_thinking_disabled branch was still dict.update-ed, so a profile that declares both templates (the shape of most extra_body examples) kept tool_stream while thinking was on and lost it while off. And the merge pruned the profile's other vLLM switch spelling for every model class, although only VllmChatModel maps `thinking` onto `enable_thinking`: on a plain ChatOpenAI profile the prune removed the only key vLLM reads, so thinking-off could no longer switch reasoning off. Route when_thinking_disabled through _merge_thinking_payload too, and replace the prune with mirroring: after the deep merge, the value the payload declares for its switch spelling is copied onto the profile's other spelling when the profile carries it. VllmChatModel collapses the pair, plain OpenAI-compatible classes send both, and either way the server reads the template's intent. No spelling is invented, a payload that spells both is left alone, and non-mapping template values are forwarded unchanged instead of raising. _VLLM_THINKING_SWITCHES is now the single definition used by the disable, toggle and dialect helpers. Docs state the merge semantics (keys are never removed; a template can only add or override) and drop the "byte-for-byte" wording; tests cover the disable template on/off, nested inheritance, non-mapping values, the raw payload for a non-normalizing class, and the no-invention rule. * fix(models): copy thinking templates on merge and document the never-remove rule Review follow-up. Templates merged into the constructor settings were stored by reference wherever the profile lacked the key, so the kwargs handed to the model class aliased the cached ModelConfig; the mirroring step now writes into that mapping, so make _merge_settings and _deep_merge_dicts deep-copy every template mapping they store. Pinned at every depth by a test that also checks the profile is unchanged. Deep-merging the legacy when_thinking_disabled template means it can no longer clear a key the profile's base extra_body sets: a base extra_body.thinking {type: enabled, budget_tokens} reaches the provider as {type: disabled, budget_tokens}, which Anthropic-style APIs reject. The synthesized disable payloads and the contract path already behaved this way. State the rule and the migration (enable-only keys belong in when_thinking_enabled) in the changelog, models/AGENTS.md and configuration.mdx, and pin it with a test for both shapes. Route the legacy synthesized OpenAI disable through _merge_thinking_payload too (identical output), narrow the docstring and guidance wording about the native-Anthropic disable, and make the two vLLM switch suites resolve the real VllmChatModel / ChatOpenAI classes so a class-gated merge would fail the case it is named after.