From 759136e0741ec904a762ddeadfdca67419363c6d Mon Sep 17 00:00:00 2001 From: Insecshin <2962479754@qq.com> Date: Fri, 2 Oct 2026 22:32:21 +0800 Subject: [PATCH] fix(config): correct Gemini official endpoint reasoning example (#6161) * fix(config): correct Gemini official endpoint reasoning example * docs: clarify official Gemini endpoint heading --- README.md | 3 ++ backend/docs/CONFIGURATION.md | 24 ++++++----- .../harness/deerflow/models/patched_openai.py | 17 +------- backend/tests/test_gemini_config_example.py | 41 +++++++++++++++++++ config.example.yaml | 27 ++++++------ 5 files changed, 72 insertions(+), 40 deletions(-) create mode 100644 backend/tests/test_gemini_config_example.py diff --git a/README.md b/README.md index da209bfb2d..83e6f14a1b 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,9 @@ Optional per-model [`request_admission`](backend/docs/CONFIGURATION.md#model-req paces requests to help stay within provider request-per-minute limits. It is disabled by default; see the linked guide to enable it. +For Google's official Gemini OpenAI-compatible endpoint, use the +[Gemini reasoning profile](backend/docs/CONFIGURATION.md#gemini-via-googles-openai-compatible-endpoint). + 1. **Clone the DeerFlow repository** ```bash diff --git a/backend/docs/CONFIGURATION.md b/backend/docs/CONFIGURATION.md index ef6a735dda..34e97932c6 100644 --- a/backend/docs/CONFIGURATION.md +++ b/backend/docs/CONFIGURATION.md @@ -269,7 +269,7 @@ models: type: enabled ``` -**Gemini with thinking via OpenAI-compatible gateway**: +#### Gemini via Google's OpenAI-compatible endpoint When routing Gemini through an OpenAI-compatible proxy (Vertex AI OpenAI compat endpoint, AI Studio, or third-party gateways) with thinking enabled, the API attaches a `thought_signature` to each tool-call object returned in the response. Every subsequent request that replays those assistant messages **must** echo those signatures back on the tool-call entries or the API returns: @@ -282,22 +282,24 @@ Standard `langchain_openai:ChatOpenAI` silently drops `thought_signature` when s ```yaml models: - - name: gemini-2.5-pro-thinking - display_name: Gemini 2.5 Pro (Thinking) + - name: gemini-3.1-pro-preview + display_name: Gemini 3.1 Pro (Thinking) use: deerflow.models.patched_openai:PatchedChatOpenAI - model: google/gemini-2.5-pro-preview # model name as expected by your gateway + model: gemini-3.1-pro-preview api_key: $GEMINI_API_KEY - base_url: https:///v1 + base_url: https://generativelanguage.googleapis.com/v1beta/openai/ max_tokens: 16384 - supports_thinking: true supports_vision: true - when_thinking_enabled: - extra_body: - thinking: - type: enabled + reasoning: + thinking: required + dialect: none + effort: + values: [minimal, low, medium, high] ``` -For Gemini accessed **without** thinking (e.g. via OpenRouter where thinking is not activated), the plain `langchain_openai:ChatOpenAI` with `supports_thinking: false` is sufficient and no patch is needed. +This example targets Google's official endpoint. Its [OpenAI compatibility API](https://ai.google.dev/gemini-api/docs/openai#thinking) accepts `reasoning_effort`; `extra_body.thinking` becomes an unsupported top-level `thinking` field and causes HTTP 400. Gemini 3.1 Pro cannot disable thinking, so `thinking: required` keeps it enabled even when a caller requests otherwise, while `dialect: none` prevents a provider-specific thinking toggle. Omitting effort uses the model's default. + +If you copied the previous example, replace its `supports_thinking`, `when_thinking_enabled`, and `when_thinking_disabled` settings with the `reasoning` block above. Third-party gateways may require different model IDs and reasoning parameters; follow that gateway's documentation instead of reusing Google's profile unchanged. **MiMo with thinking via OpenAI-compatible API**: diff --git a/backend/packages/harness/deerflow/models/patched_openai.py b/backend/packages/harness/deerflow/models/patched_openai.py index cf98141de5..54c44930cb 100644 --- a/backend/packages/harness/deerflow/models/patched_openai.py +++ b/backend/packages/harness/deerflow/models/patched_openai.py @@ -39,21 +39,8 @@ class PatchedChatOpenAI(ChatOpenAI): from ``AIMessage.additional_kwargs["tool_calls"]`` into the serialised request payload before it is sent to the API. - Usage in ``config.yaml``:: - - - name: gemini-2.5-pro-thinking - display_name: Gemini 2.5 Pro (Thinking) - use: deerflow.models.patched_openai:PatchedChatOpenAI - model: google/gemini-2.5-pro-preview - api_key: $GEMINI_API_KEY - base_url: https:///v1 - max_tokens: 16384 - supports_thinking: true - supports_vision: true - when_thinking_enabled: - extra_body: - thinking: - type: enabled + See the Gemini example in ``config.example.yaml`` for Google's official + OpenAI-compatible endpoint and its reasoning configuration. """ def _get_request_payload( diff --git a/backend/tests/test_gemini_config_example.py b/backend/tests/test_gemini_config_example.py new file mode 100644 index 0000000000..693462e975 --- /dev/null +++ b/backend/tests/test_gemini_config_example.py @@ -0,0 +1,41 @@ +"""Exercise the shipped Gemini example through the model factory and OpenAI SDK.""" + +import json +from pathlib import Path + +import httpx +import pytest +import yaml + +from deerflow.config.app_config import AppConfig +from deerflow.config.model_config import ModelConfig +from deerflow.config.sandbox_config import SandboxConfig +from deerflow.models.factory import create_chat_model + + +@pytest.mark.parametrize("thinking_enabled,effort", [(True, None), (True, "medium"), (True, "high"), (False, "minimal")]) +def test_gemini_example_request(thinking_enabled, effort): + example = (Path(__file__).resolve().parents[2] / "config.example.yaml").read_text(encoding="utf-8") + block = example.split(" # Example: Gemini model via ", 1)[1].split("\n\n", 1)[0] + lines = block[block.index(" # - name:") :].splitlines() + settings = yaml.safe_load("\n".join(line.removeprefix(" # ") for line in lines))[0] + settings["api_key"] = "test-key" + config = AppConfig(models=[ModelConfig(**settings)], sandbox=SandboxConfig(use="deerflow.sandbox.local:LocalSandboxProvider")) + requests = [] + + def respond(request): + requests.append(request) + return httpx.Response(200, json={"id": "test", "object": "chat.completion", "created": 0, "model": settings["model"], "choices": [{"index": 0, "message": {"role": "assistant", "content": "OK"}, "finish_reason": "stop"}]}) + + with httpx.Client(transport=httpx.MockTransport(respond)) as client: + model = create_chat_model(app_config=config, attach_tracing=False, thinking_enabled=thinking_enabled, reasoning_effort=effort, http_client=client) + assert model.invoke("Hello").content == "OK" + + assert len(requests) == 1 + payload = json.loads(requests[0].content) + assert "thinking" not in payload + assert payload.get("reasoning_effort") == effort + if effort is None: + assert "reasoning_effort" not in payload + assert payload["model"] == "gemini-3.1-pro-preview" + assert str(requests[0].url) == "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions" diff --git a/config.example.yaml b/config.example.yaml index 58fad20c9e..4cc3ad9224 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -430,31 +430,30 @@ models: # context_window: 1048576 # supports_vision: true - # Example: Gemini model via OpenAI-compatible gateway (with thinking support) + # Example: Gemini model via Google's official OpenAI-compatible endpoint # Use PatchedChatOpenAI so that tool-call thought_signature values on tool_calls # are preserved across multi-turn tool-call conversations — required by the # Gemini API when thinking is enabled. See: # https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures - # - name: gemini-2.5-pro-thinking - # display_name: Gemini 2.5 Pro (Thinking) + # Google accepts reasoning_effort, not extra_body.thinking. Gemini 3.1 Pro + # cannot disable thinking; dialect: none avoids emitting a thinking toggle. + # https://ai.google.dev/gemini-api/docs/openai#thinking + # - name: gemini-3.1-pro-preview + # display_name: Gemini 3.1 Pro (Thinking) # use: deerflow.models.patched_openai:PatchedChatOpenAI - # model: google/gemini-2.5-pro-preview # model name as expected by your gateway + # model: gemini-3.1-pro-preview # api_key: $GEMINI_API_KEY - # base_url: https:///v1 + # base_url: https://generativelanguage.googleapis.com/v1beta/openai/ # request_timeout: 600.0 # max_retries: 2 # max_tokens: 16384 # context_window: 1048576 - # supports_thinking: true # supports_vision: true - # when_thinking_enabled: - # extra_body: - # thinking: - # type: enabled - # when_thinking_disabled: - # extra_body: - # thinking: - # type: disabled + # reasoning: + # thinking: required + # dialect: none + # effort: + # values: [minimal, low, medium, high] # Example: Xiaomi MiMo model (with thinking support) # MiMo thinking mode returns reasoning_content and requires that field to be