From 3aa144a377a37269e476f1123f1c7887668201c1 Mon Sep 17 00:00:00 2001 From: yangfucheng123 <140314128+yangfucheng123@users.noreply.github.com> Date: Mon, 19 Oct 2026 15:45:56 +0800 Subject: [PATCH] fix(managed-models): map DeepSeek "minimal" effort to "low" (#6717) The official DeepSeek Chat Completions endpoint rejects the generic "minimal" effort token (issue #4514). The declared effort contract for DeepSeek models now advertises the provider's real vocabulary (low/medium/high/max/xhigh), defaults to "low", and aliases the generic "minimal" preset onto "low" so the "flash" dialogue mode serializes a value the endpoint accepts. Co-authored-by: Willem Jiang --- .../config/managed_model_providers.py | 2 +- .../harness/deerflow/models/AGENTS.md | 2 +- backend/tests/test_managed_deepseek.py | 30 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/backend/packages/harness/deerflow/config/managed_model_providers.py b/backend/packages/harness/deerflow/config/managed_model_providers.py index a69e143ccb..14ffa42a8b 100644 --- a/backend/packages/harness/deerflow/config/managed_model_providers.py +++ b/backend/packages/harness/deerflow/config/managed_model_providers.py @@ -95,7 +95,7 @@ def _capabilities(provider: str, model: str) -> dict[str, Any]: when_thinking_enabled={"extra_body": {"thinking": {"type": "enabled"}}}, when_thinking_disabled={"extra_body": {"thinking": {"type": "disabled"}}}, ) - result["reasoning"]["effort"] = {"values": ["high", "max"], "default": "high", "aliases": {"minimal": "high", "low": "high", "medium": "high"}} + result["reasoning"]["effort"] = {"values": ["low", "medium", "high", "max", "xhigh"], "default": "low", "aliases": {"minimal": "low"}} elif provider in {"zai", "zhipu-cn"} and model in {"glm-4.5", "glm-4.6", "glm-4.7", "glm-5.3-flash"}: result.update( reasoning={"thinking": "optional", "dialect": "openai_extra_body", "history": "preserve"}, diff --git a/backend/packages/harness/deerflow/models/AGENTS.md b/backend/packages/harness/deerflow/models/AGENTS.md index 731a6e53f5..f9a861d1c0 100644 --- a/backend/packages/harness/deerflow/models/AGENTS.md +++ b/backend/packages/harness/deerflow/models/AGENTS.md @@ -218,7 +218,7 @@ Direct `AppConfig.from_file()` continues to read only operator configuration. `config/managed_model_providers.py` is the sole registry for provider IDs, labels, endpoints, protocols, authentication parameter mappings, adapters and request defaults. Capability rules match exact verified model IDs; unknown preset IDs -have an unsupported-thinking contract, no vision and no inferred context window. +have an unsupported-thinking contract, no vision and no inferred context window. Verified DeepSeek presets declare an effort vocabulary of `low`/`medium`/`high`/`max`/`xhigh` (default `low`) and alias the generic `minimal` preset onto `low`, so a saved per-agent `minimal` default serializes as a value the official endpoint accepts (issue #4514). Preset drafts cannot supply endpoints or adapter paths. Legacy custom `openai-compatible` records retain endpoint-based official DeepSeek detection; never infer that adapter from a model-name substring. Reads do not migrate the diff --git a/backend/tests/test_managed_deepseek.py b/backend/tests/test_managed_deepseek.py index 0c5129db9a..936669b5de 100644 --- a/backend/tests/test_managed_deepseek.py +++ b/backend/tests/test_managed_deepseek.py @@ -212,6 +212,36 @@ async def test_saved_model_streaming_replays_reasoning_and_honors_token_limit(st assert model_config.supports_reasoning_effort is True +@pytest.mark.asyncio +@pytest.mark.parametrize( + "effort,expected", + [("minimal", "low"), ("low", "low"), ("medium", "medium"), ("high", "high"), ("max", "max"), ("xhigh", "xhigh")], +) +async def test_deepseek_effort_contract_maps_generic_effort_values(store, provider, effort, expected): + saved = store.save(ManagedModel(provider="deepseek", model="deepseek-flash", api_key=_KEY), expected_revision=None) + config = merge_managed_models(_base_config()) + model_config = config.get_model_config(saved.name) + + # The official endpoint rejects the generic "minimal" token (issue #4514), + # so the declared contract advertises the provider's real vocabulary and + # aliases "minimal" onto "low". Parametrized over every accepted value so + # a future rename/drop fails here, not at request time. + assert model_config.reasoning is not None + assert model_config.reasoning.effort is not None + assert model_config.reasoning.effort.values == ["low", "medium", "high", "max", "xhigh"] + assert model_config.reasoning.effort.default == "low" + assert model_config.reasoning.effort.aliases == {"minimal": "low"} + assert model_config.supports_reasoning_effort is True + + model = create_chat_model(saved.name, thinking_enabled=False, reasoning_effort=effort, app_config=config, attach_tracing=False) + await _collect(model, [HumanMessage(content="Hello")]) + + assert len(provider.requests) == 1 + body = provider.requests[0][1] + assert body["reasoning_effort"] == expected + assert body["thinking"] == {"type": "disabled"} + + @pytest.mark.parametrize( "base_url", [