From 92fc2b592eff07ba836d48c693f202b3945e5d80 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Wed, 17 Jun 2026 10:03:03 +0800 Subject: [PATCH] fix: tool definition does not pass back to gemini properly, causing repeated tool calls. fixes: #8789 fixes: #8773 fixes: #7111 fixes: #6402 fixes: #7684 --- .../core/provider/sources/gemini_source.py | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index 0c5817489..bd87391be 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -349,12 +349,6 @@ class ProviderGoogleGenAI(Provider): contents.append(content_cls(parts=part)) gemini_contents: list[types.Content] = [] - native_tool_enabled = any( - [ - self.provider_config.get("gm_native_coderunner", False), - self.provider_config.get("gm_native_search", False), - ], - ) for message in payloads["messages"]: role, content = message["role"], message.get("content") @@ -377,11 +371,10 @@ class ProviderGoogleGenAI(Provider): append_or_extend(gemini_contents, parts, types.UserContent) elif role == "assistant": + parts = [] if isinstance(content, str): - parts = [types.Part.from_text(text=content)] - append_or_extend(gemini_contents, parts, types.ModelContent) + parts.append(types.Part.from_text(text=content)) elif isinstance(content, list): - parts = [] thinking_signature = None text = "" for part in content: @@ -400,16 +393,25 @@ class ProviderGoogleGenAI(Provider): exc_info=True, ) thinking_signature = None - parts.append( - types.Part( - text=text, - thought_signature=thinking_signature, - ) - ) - append_or_extend(gemini_contents, parts, types.ModelContent) - elif not native_tool_enabled and "tool_calls" in message: - parts = [] + if not text and thinking_signature and "tool_calls" in message and any( + tool.get("extra_content", {}).get("google", {}).get( + "thought_signature" + ) + for tool in message["tool_calls"] + ): + # If the main content is empty but tool calls have thought signatures, + # skip adding an empty text part to deduplicate the thinking signature in the main content and tool calls. + pass + else: + parts.append( + types.Part( + text=text, + thought_signature=thinking_signature, + ) + ) + + if "tool_calls" in message: for tool in message["tool_calls"]: part = types.Part.from_function_call( name=tool["function"]["name"], @@ -427,17 +429,13 @@ class ProviderGoogleGenAI(Provider): if ts_bs64: part.thought_signature = base64.b64decode(ts_bs64) parts.append(part) - append_or_extend(gemini_contents, parts, types.ModelContent) - else: - logger.warning("assistant 角色的消息内容为空,已添加空格占位") - if native_tool_enabled and "tool_calls" in message: - logger.warning( - "检测到启用Gemini原生工具,且上下文中存在函数调用,建议使用 /reset 重置上下文", - ) - parts = [types.Part.from_text(text=" ")] - append_or_extend(gemini_contents, parts, types.ModelContent) - elif role == "tool" and not native_tool_enabled: + if not parts: + parts = [types.Part.from_text(text=" ")] + + append_or_extend(gemini_contents, parts, types.ModelContent) + + elif role == "tool": func_name = message.get("name", message["tool_call_id"]) part = types.Part.from_function_response( name=func_name,