Skip to content

fix(agent): unwrap nested tool call arguments - #10076

Draft
NayukiChiba wants to merge 1 commit into
AstrBotDevs:masterfrom
NayukiChiba:fix/issue-10074-tool-arguments
Draft

fix(agent): unwrap nested tool call arguments#10076
NayukiChiba wants to merge 1 commit into
AstrBotDevs:masterfrom
NayukiChiba:fix/issue-10074-tool-arguments

Conversation

@NayukiChiba

@NayukiChiba NayukiChiba commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Fixes #10074

当工具调用参数被额外包装为 {"arguments": {"command": "..."}},甚至出现多层嵌套时,核心会直接将外层 arguments 作为关键字参数传给工具,导致参数不匹配,工具无法执行。

Modifications / 改动点

  • 在参数过滤和工具钩子执行前,展开多余的 arguments 包装层。
  • 仅处理只有 arguments 一个键且值为对象的包装结构。
  • 保留工具明确定义的 arguments 参数及现有权限检查。
  • 新增 21 个回归用例,覆盖流式与非流式调用、本地 Shell、插件工具及边界情况。
  • This is NOT a breaking change. / 这不是一个破坏性变更。

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

(astrbot) [arch@laptop Astrbot]$ python -m pytest tests/test_tool_loop_agent_runner.py -k "local_sh
ell_accepts_wrapped_tool_arguments or handler_tool_unwraps_arguments_before_parameter_filtering or 
tool_arguments_preserve_non_wrapper_inputs" -v
======================================= test session starts =======================================
platform linux -- Python 3.12.14, pytest-9.1.1, pluggy-1.6.0 -- /home/arch/NayukiChiba/Astrbot/.venv/bin/python
cachedir: .pytest_cache
rootdir: /home/arch/NayukiChiba/Astrbot
configfile: pyproject.toml
plugins: cov-7.1.0, anyio-4.15.1, asyncio-1.4.0
asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 74 items / 53 deselected / 21 selected                                                  

tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[admin-0-False] PASSED [  4%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[admin-0-True] PASSED [  9%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[admin-1-False] PASSED [ 14%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[admin-1-True] PASSED [ 19%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[admin-2-False] PASSED [ 23%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[admin-2-True] PASSED [ 28%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[member-0-False] PASSED [ 33%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[member-0-True] PASSED [ 38%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[member-1-False] PASSED [ 42%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[member-1-True] PASSED [ 47%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[member-2-False] PASSED [ 52%]
tests/test_tool_loop_agent_runner.py::test_local_shell_accepts_wrapped_tool_arguments[member-2-True] PASSED [ 57%]
tests/test_tool_loop_agent_runner.py::test_handler_tool_unwraps_arguments_before_parameter_filtering[1] PASSED [ 61%]
tests/test_tool_loop_agent_runner.py::test_handler_tool_unwraps_arguments_before_parameter_filtering[2] PASSED [ 66%]
tests/test_tool_loop_agent_runner.py::test_tool_arguments_preserve_non_wrapper_inputs[declared-arguments] PASSED [ 71%]
tests/test_tool_loop_agent_runner.py::test_tool_arguments_preserve_non_wrapper_inputs[mixed-top-level-keys] PASSED [ 76%]
tests/test_tool_loop_agent_runner.py::test_tool_arguments_preserve_non_wrapper_inputs[string-wrapper] PASSED [ 80%]
tests/test_tool_loop_agent_runner.py::test_tool_arguments_preserve_non_wrapper_inputs[null-wrapper] PASSED [ 85%]
tests/test_tool_loop_agent_runner.py::test_tool_arguments_preserve_non_wrapper_inputs[list-wrapper] PASSED [ 90%]
tests/test_tool_loop_agent_runner.py::test_tool_arguments_preserve_non_wrapper_inputs[no-declared-properties] PASSED [ 95%]
tests/test_tool_loop_agent_runner.py::test_tool_arguments_preserve_non_wrapper_inputs[nested-parameter-value] PASSED [100%]

astrbot/core/utils/tencent_record_helper.py:4
  /home/arch/NayukiChiba/Astrbot/astrbot/core/utils/tencent_record_helper.py:4: DeprecationWarning: 'audioop' is deprecated and slated for removal in Python 3.13
    import audioop

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html

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

Ensure tool calls with redundant nested argument wrappers are normalized without changing valid argument handling.

Bug Fixes:

  • Normalize tool-call arguments by removing redundant, recursively nested arguments wrappers before parameter filtering and hook execution, allowing affected tools to execute correctly.

Enhancements:

  • Preserve explicitly declared arguments parameters and non-wrapper argument structures while retaining existing permission checks.

Tests:

  • Add regression coverage for nested arguments across streaming and non-streaming execution, local shell and handler tools, permission behavior, and wrapper edge cases.

@sourcery-ai sourcery-ai Bot left a comment

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.

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

Sourcery assessment

Needs a human reviewer. If the unwrapping condition is wrong, a previously rejected or malformed tool call could invoke a handler with unintended arguments, including executing an unintended local shell command. Reverting prevents future calls from taking that path, but any side effects from commands or other tools that already ran would remain.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@NayukiChiba
NayukiChiba marked this pull request as draft September 14, 2026 02:45
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] AstrBot 核心 v4.28.0 与所用模型在"函数调用参数格式"上不兼容。

1 participant