Skip to content

fix: preserve Gemini thought_signature in tool calls and add ddgs dependency #26

Closed
SoufianoDev wants to merge 1 commit into
HKUDS:mainfrom
SoufianoDev:main
Closed

fix: preserve Gemini thought_signature in tool calls and add ddgs dependency #26
SoufianoDev wants to merge 1 commit into
HKUDS:mainfrom
SoufianoDev:main

Conversation

@SoufianoDev
Copy link
Copy Markdown
Contributor

Summary

Fixes #25 — two bugs that cause Gemini provider to fail when using tool calls.

Bug 1: Missing ddgs dependency

WebSearchTool imports ddgs/duckduckgo_search at runtime, but the package was not listed in pyproject.toml or agent/requirements.txt. This caused web_search to always return a JSON error, which then triggered Bug 2.

Bug 2: Gemini thought_signature not preserved across turns

Gemini's API returns extra_content.google.thought_signature on tool call responses. This signature must be echoed back in subsequent conversation turns. However, ChatOpenAI from langchain-openai strips extra_content during parsing. When the conversation history was sent back to Gemini without the signature, it returned a 400 INVALID_ARGUMENT error:

Function call is missing a thought_signature in functionCall parts.

Changes

  • pyproject.toml / agent/requirements.txt — Added ddgs>=6.0.0 dependency
  • agent/src/providers/llm.py — Added GeminiChatOpenAI subclass of ChatOpenAI that overrides _create_chat_result() to capture extra_content from raw API responses and store it in AIMessage.additional_kwargs["tool_call_extras"]. Updated build_llm() to use this class when LANGCHAIN_PROVIDER=gemini.
  • agent/src/providers/chat.py — Added thought_signature field to ToolCallRequest. Updated _parse_response() to extract signatures from tool_call_extras. Updated stream_chat() to bypass streaming for Gemini (since stream chunks don't preserve extra_content) and use chat() directly.
  • agent/src/agent/context.py — Updated format_assistant_tool_calls() to include extra_content.google.thought_signature in formatted assistant messages when present.
  • agent/tests/test_gemini_thought_signature.py — Added 23 tests covering the full thought_signature preservation flow, including end-to-end tests with real OpenAI SDK Pydantic models.

Testing

Platform Tests Result
Linux (native) 23 new + 35 existing 🗹 All pass
Wine (Windows compat) 23 new + 35 existing 🗹 All pass
Wine (live) WebSearchTool with ddgs 🗹 Returns results

@SoufianoDev SoufianoDev changed the title fix: preserve Gemini thought_signature in tool calls and add ddgs dependency (#25) fix: preserve Gemini thought_signature in tool calls and add ddgs dependency Apr 12, 2026
@warren618
Copy link
Copy Markdown
Collaborator

Hi @SoufianoDev, thanks for the thorough PR and the detailed tests!

Unfortunately, this approach won't work due to a limitation in langchain-openai's message conversion pipeline. We verified this by running the code against a real API:

When the formatted assistant message (with extra_content) is passed back to ChatOpenAI.invoke(), LangChain converts the dict through convert_to_messages()AIMessage_convert_message_to_dict(). During this round-trip, all non-standard fields on tool calls are stripped:

# langchain-openai/chat_models/base.py
tool_call_supported_props = {"id", "type", "function"}
message_dict["tool_calls"] = [
    {k: v for k, v in tool_call.items() if k in tool_call_supported_props}
    for tool_call in message_dict["tool_calls"]
]

So even though the PR correctly captures and re-attaches thought_signature, it gets silently dropped before reaching the Gemini API — the 400 error would still occur.

We've documented this on the issue (#25) — this is an upstream LangChain limitation that can't be worked around from the application side. The current recommendation for Gemini users is to switch to a provider that doesn't require thought_signature (DeepSeek, Qwen, etc.).

The ddgs dependency fix has already been merged in b17aeec.

Appreciate the effort — happy to revisit if LangChain adds support for passthrough fields!

@warren618 warren618 closed this Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]

2 participants