mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-22 16:06:50 +00:00
Merge branch 'main' into release/2.0-rc
This commit is contained in:
@@ -30,6 +30,7 @@ def _make_model(
|
||||
supports_thinking: bool = False,
|
||||
supports_reasoning_effort: bool = False,
|
||||
when_thinking_enabled: dict | None = None,
|
||||
when_thinking_disabled: dict | None = None,
|
||||
thinking: dict | None = None,
|
||||
max_tokens: int | None = None,
|
||||
) -> ModelConfig:
|
||||
@@ -43,6 +44,7 @@ def _make_model(
|
||||
supports_thinking=supports_thinking,
|
||||
supports_reasoning_effort=supports_reasoning_effort,
|
||||
when_thinking_enabled=when_thinking_enabled,
|
||||
when_thinking_disabled=when_thinking_disabled,
|
||||
thinking=thinking,
|
||||
supports_vision=False,
|
||||
)
|
||||
@@ -244,6 +246,136 @@ def test_thinking_disabled_no_when_thinking_enabled_does_nothing(monkeypatch):
|
||||
assert captured.get("reasoning_effort") is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# when_thinking_disabled config
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_when_thinking_disabled_takes_precedence_over_hardcoded_disable(monkeypatch):
|
||||
"""When when_thinking_disabled is set, it takes full precedence over the
|
||||
hardcoded disable logic (extra_body.thinking.type=disabled etc.)."""
|
||||
wte = {"extra_body": {"thinking": {"type": "enabled", "budget_tokens": 10000}}}
|
||||
wtd = {"extra_body": {"thinking": {"type": "disabled"}}, "reasoning_effort": "low"}
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"custom-disable",
|
||||
supports_thinking=True,
|
||||
supports_reasoning_effort=True,
|
||||
when_thinking_enabled=wte,
|
||||
when_thinking_disabled=wtd,
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg)
|
||||
|
||||
captured: dict = {}
|
||||
|
||||
class CapturingModel(FakeChatModel):
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
BaseChatModel.__init__(self, **kwargs)
|
||||
|
||||
monkeypatch.setattr(factory_module, "resolve_class", lambda path, base: CapturingModel)
|
||||
|
||||
factory_module.create_chat_model(name="custom-disable", thinking_enabled=False)
|
||||
|
||||
assert captured.get("extra_body") == {"thinking": {"type": "disabled"}}
|
||||
# User overrode the hardcoded "minimal" with "low"
|
||||
assert captured.get("reasoning_effort") == "low"
|
||||
|
||||
|
||||
def test_when_thinking_disabled_not_used_when_thinking_enabled(monkeypatch):
|
||||
"""when_thinking_disabled must have no effect when thinking_enabled=True."""
|
||||
wte = {"extra_body": {"thinking": {"type": "enabled"}}}
|
||||
wtd = {"extra_body": {"thinking": {"type": "disabled"}}}
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"wtd-ignored",
|
||||
supports_thinking=True,
|
||||
when_thinking_enabled=wte,
|
||||
when_thinking_disabled=wtd,
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg)
|
||||
|
||||
captured: dict = {}
|
||||
|
||||
class CapturingModel(FakeChatModel):
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
BaseChatModel.__init__(self, **kwargs)
|
||||
|
||||
monkeypatch.setattr(factory_module, "resolve_class", lambda path, base: CapturingModel)
|
||||
|
||||
factory_module.create_chat_model(name="wtd-ignored", thinking_enabled=True)
|
||||
|
||||
# when_thinking_enabled should apply, NOT when_thinking_disabled
|
||||
assert captured.get("extra_body") == {"thinking": {"type": "enabled"}}
|
||||
|
||||
|
||||
def test_when_thinking_disabled_without_when_thinking_enabled_still_applies(monkeypatch):
|
||||
"""when_thinking_disabled alone (no when_thinking_enabled) should still apply its settings."""
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"wtd-only",
|
||||
supports_thinking=True,
|
||||
supports_reasoning_effort=True,
|
||||
when_thinking_disabled={"reasoning_effort": "low"},
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg)
|
||||
|
||||
captured: dict = {}
|
||||
|
||||
class CapturingModel(FakeChatModel):
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
BaseChatModel.__init__(self, **kwargs)
|
||||
|
||||
monkeypatch.setattr(factory_module, "resolve_class", lambda path, base: CapturingModel)
|
||||
|
||||
factory_module.create_chat_model(name="wtd-only", thinking_enabled=False)
|
||||
|
||||
# when_thinking_disabled is now gated independently of has_thinking_settings
|
||||
assert captured.get("reasoning_effort") == "low"
|
||||
|
||||
|
||||
def test_when_thinking_disabled_excluded_from_model_dump(monkeypatch):
|
||||
"""when_thinking_disabled must not leak into the model constructor kwargs."""
|
||||
wte = {"extra_body": {"thinking": {"type": "enabled"}}}
|
||||
wtd = {"extra_body": {"thinking": {"type": "disabled"}}}
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"no-leak-wtd",
|
||||
supports_thinking=True,
|
||||
when_thinking_enabled=wte,
|
||||
when_thinking_disabled=wtd,
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg)
|
||||
|
||||
captured: dict = {}
|
||||
|
||||
class CapturingModel(FakeChatModel):
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
BaseChatModel.__init__(self, **kwargs)
|
||||
|
||||
monkeypatch.setattr(factory_module, "resolve_class", lambda path, base: CapturingModel)
|
||||
|
||||
factory_module.create_chat_model(name="no-leak-wtd", thinking_enabled=True)
|
||||
|
||||
# when_thinking_disabled value must NOT appear as a raw key
|
||||
assert "when_thinking_disabled" not in captured
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# reasoning_effort stripping
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -768,3 +900,44 @@ def test_openai_responses_api_settings_are_passed_to_chatopenai(monkeypatch):
|
||||
|
||||
assert captured.get("use_responses_api") is True
|
||||
assert captured.get("output_version") == "responses/v1"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Duplicate keyword argument collision (issue #1977)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_no_duplicate_kwarg_when_reasoning_effort_in_config_and_thinking_disabled(monkeypatch):
|
||||
"""When reasoning_effort is set in config.yaml (extra field) AND the thinking-disabled
|
||||
path also injects reasoning_effort=minimal into kwargs, the factory must not raise
|
||||
TypeError: got multiple values for keyword argument 'reasoning_effort'."""
|
||||
wte = {"extra_body": {"thinking": {"type": "enabled", "budget_tokens": 5000}}}
|
||||
# ModelConfig.extra="allow" means extra fields from config.yaml land in model_dump()
|
||||
model = ModelConfig(
|
||||
name="doubao-model",
|
||||
display_name="Doubao 1.8",
|
||||
description=None,
|
||||
use="deerflow.models.patched_deepseek:PatchedChatDeepSeek",
|
||||
model="doubao-seed-1-8-250315",
|
||||
reasoning_effort="high", # user-set extra field in config.yaml
|
||||
supports_thinking=True,
|
||||
supports_reasoning_effort=True,
|
||||
when_thinking_enabled=wte,
|
||||
supports_vision=False,
|
||||
)
|
||||
cfg = _make_app_config([model])
|
||||
|
||||
captured: dict = {}
|
||||
|
||||
class CapturingModel(FakeChatModel):
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
BaseChatModel.__init__(self, **kwargs)
|
||||
|
||||
_patch_factory(monkeypatch, cfg, model_class=CapturingModel)
|
||||
|
||||
# Must not raise TypeError
|
||||
factory_module.create_chat_model(name="doubao-model", thinking_enabled=False)
|
||||
|
||||
# kwargs (runtime) takes precedence: thinking-disabled path sets reasoning_effort=minimal
|
||||
assert captured.get("reasoning_effort") == "minimal"
|
||||
|
||||
Reference in New Issue
Block a user