Skip to content

fix: skip tool calls with None name/id to prevent crash and 400 error#8607

Open
lingyun14beta wants to merge 3 commits into
AstrBotDevs:masterfrom
lingyun14beta:fix/tool-call-none-name-id
Open

fix: skip tool calls with None name/id to prevent crash and 400 error#8607
lingyun14beta wants to merge 3 commits into
AstrBotDevs:masterfrom
lingyun14beta:fix/tool-call-none-name-id

Conversation

@lingyun14beta
Copy link
Copy Markdown
Contributor

@lingyun14beta lingyun14beta commented Jun 5, 2026

Fixes #8546 — 当 LLM 返回 function.name=None 的工具调用时系统崩溃

当使用 OpenAI 兼容接口代理 Claude 等模型时,中间层格式转换可能导致工具调用的 function.nameid 字段丢失(变为 None),引发连锁崩溃:

  1. entities.py 中 Pydantic 校验失败:ValidationError: name must be a valid string
  2. 跳过后 tool_use_id=None 被写入对话历史,下一轮请求触发 400: missing tool_use_id
  3. 对话中断,LLM"忘记"上下文

Modifications / 改动点

  • astrbot/core/provider/sources/openai_source.py:解析工具调用时检查 functionnameid 是否为空,任一为空则跳过并打 warning;所有工具调用均被跳过时不设置 role=tool,避免触发误报的 EmptyModelOutputError
  • astrbot/core/agent/runners/tool_loop_agent_runner.py :工具执行循环最开头检查 name/id,为空则跳过整个条目(包括不产生 tool_result block),防止 tool_use_id=None 写入对话历史
  • astrbot/core/provider/entities.pyto_openai_tool_calls()to_openai_to_calls_model() 两个方法均加入 None guard,防止其他 provider source 漏过时触发 Pydantic 崩溃;同步修复旧方法 to_openai_tool_calls() 此前缺少 guard 的问题
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

image

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Handle malformed or incomplete tool calls more robustly to prevent crashes and invalid history entries when function name or id is missing.

Bug Fixes:

  • Skip tool calls with missing function name or id in OpenAI provider parsing to avoid validation errors and bad tool_use_ids.
  • Guard conversion to OpenAI tool call dicts/models against None names/ids to prevent Pydantic crashes and inconsistent extra_content mapping.
  • Ignore tool loop entries with empty tool name or id to avoid writing invalid tool_call results into the conversation history.

Enhancements:

  • Treat responses where all function tool calls are skipped as non-tool-call responses and emit warnings for easier debugging.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. labels Jun 5, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces safety checks to skip tool calls with missing names, IDs, or function fields across several files, preventing errors when processing incomplete tool call responses. The reviewer noted that accessing parallel lists (self.tools_call_name and self.tools_call_ids) by index in astrbot/core/provider/entities.py could raise an IndexError if the lists are mismatched in length. They suggested refactoring this duplicated logic into a shared helper function to safely retrieve tool calls.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 454 to +457
for idx, tool_call_arg in enumerate(self.tools_call_args):
tool_name = self.tools_call_name[idx]
tool_id = self.tools_call_ids[idx]
if not tool_name or not tool_id:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Accessing self.tools_call_name and self.tools_call_ids by index idx can raise an IndexError if the parallel lists are mismatched in length. Since this similar logic is implemented in multiple places, refactor it into a shared helper function to avoid code duplication.

Suggested change
for idx, tool_call_arg in enumerate(self.tools_call_args):
tool_name = self.tools_call_name[idx]
tool_id = self.tools_call_ids[idx]
if not tool_name or not tool_id:
for tool_id, tool_name, tool_call_arg in self._get_safe_tool_calls():
References
  1. When implementing similar functionality for different cases, refactor the logic into a shared helper function to avoid code duplication.

Comment on lines 479 to +482
for idx, tool_call_arg in enumerate(self.tools_call_args):
tool_name = self.tools_call_name[idx]
tool_id = self.tools_call_ids[idx]
if not tool_name or not tool_id:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Accessing self.tools_call_name and self.tools_call_ids by index idx can raise an IndexError if the parallel lists are mismatched in length. Since this similar logic is implemented in multiple places, refactor it into a shared helper function to avoid code duplication.

Suggested change
for idx, tool_call_arg in enumerate(self.tools_call_args):
tool_name = self.tools_call_name[idx]
tool_id = self.tools_call_ids[idx]
if not tool_name or not tool_id:
for tool_id, tool_name, tool_call_arg in self._get_safe_tool_calls():
References
  1. When implementing similar functionality for different cases, refactor the logic into a shared helper function to avoid code duplication.

@lingyun14beta lingyun14beta changed the title Fix/tool call none name fix: skip tool calls with None name/id to prevent crash and 400 error Jun 5, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

当llm调用工具时把function.name设置成None会崩溃

1 participant