Repository navigation
fix(core): add ToolUseBlock.getSuggestedRules() implementation - #2916
ShuiMu-peng wants to merge 7 commits into
Conversation
Add complete implementation of ToolUseBlock.getSuggestedRules() as documented: - Add suggestedRules field to ToolUseBlock with JSON serialization support - Implement withSuggestedRules() and withStateAndSuggestedRules() methods - Update ReActAgent to invoke getSuggestedRules() and attach rules to ToolUseBlock - Add comprehensive unit tests for suggestedRules functionality - Update PermissionHITLExample to use getSuggestedRules() Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
| List<PermissionRule> suggested = decision.getSuggestedRules(); | ||
| ToolUseBlock toolUseBlock = use; | ||
| if (suggested != null && !suggested.isEmpty()) { | ||
| toolUseBlock = use.withSuggestedRules(suggested); |
There was a problem hiding this comment.
[P2] 轻量权限路径也需要传递 suggested rules
这里的复制只发生在 useEngine == true 的分支。默认构建的 ReActAgent 使用 trivial PermissionContextState,会进入下面的轻量权限路径;如果自定义 ToolBase.checkPermissions() 返回 PermissionDecision.ask(...).withSuggestedRules(...),case ASK 仍用原始 use 构造 PermissionVerdict,这些规则会被静默丢失。
我用针对性测试复现后,完整 PermissionEngine 路径可以拿到 1 条规则,但默认轻量路径得到的是 0 条。这样本次 HITL 确认虽然仍能进行,暂停返回的 Msg 和 RequireUserConfirmEvent 中 getSuggestedRules() 却为空,调用方无法接受工具生成的规则,后续同类调用仍会重复询问。
建议让轻量路径的 ASK 分支也把 decision.getSuggestedRules() 附加到 ToolUseBlock,并补一个默认 agent 下的端到端回归测试。
…ules-final' into fix/add-tooluseblock-suggested-rules-final
|
This PR currently conflicts with git fetch upstream
git checkout fix/add-tooluseblock-suggested-rules-final
git rebase upstream/main
# resolve conflicts, then:
git push --force-with-leaseThis is a one-time reminder. Once the conflicts are resolved, comment Automated notification by github-manager-bot |
oss-maintainer
left a comment
There was a problem hiding this comment.
Summary
Plumbs the permission engine / tool suggested rules through to ToolUseBlock so an approved ASK can register rules without a second prompt, and keeps withState(...) preserving the new field. API additions are non-breaking (the old constructors are kept and delegate), JSON round-trip is covered, and the default-to-empty-list normalisation is the right call.
Findings
- [Warning]
updateToolCallStatesAndRulesclearssuggestedRulesfor updated ids that are not pending (and via the single-id convenience overload) — see inline comment. - [Info]
Collectors.toMapis NPE-sensitive; the safety depends on a normalisation one class away. - [Info] naming + a test gap on the context write-back path.
Suggestions
The core fix in evaluateOne (both engine and lightweight paths now carry withSuggestedRulesIfExist) looks correct and is the part the issue was about.
Verdict
Comment only — one behavioural edge to settle before approval. The PR also still conflicts with main.
Automated review by github-manager-bot
| for (ContentBlock block : m.getContent()) { | ||
| if (block instanceof ToolUseBlock t && updates.containsKey(t.getId())) { | ||
| rebuilt.add(t.withState(updates.get(t.getId()))); | ||
| List<PermissionRule> rules = pendToolMap.getOrDefault(t.getId(), List.of()); |
There was a problem hiding this comment.
getOrDefault(id, List.of()) overwrites the field unconditionally via withStateAndSuggestedRules, so every gated-but-not-pending block (the ALLOWED branch of stateUpdates) has any rules it previously carried erased. Before this change the same call site used withState(...), which preserved every other field. The same happens from updateToolCallState(String, ToolCallState) (now delegating with an empty map), so a single-id state transition also clears rules. Suggest only writing rules when the id is actually present, e.g. keep t.withState(...) for ids missing from pendToolMap, so "no information" and "no rules" stay distinguishable. If clearing is really intended, a test that transitions an ASKING block with rules to ALLOWED through this method would make that explicit.
| Map<String, List<PermissionRule>> pendToolMap = | ||
| pending.stream() | ||
| .collect( | ||
| Collectors.toMap( |
There was a problem hiding this comment.
Collectors.toMap throws NPE when the value mapper returns null. Today ToolUseBlock#getSuggestedRules normalises null to an empty list so this is safe, but it is only safe because of a guarantee one class away — an explicit loop (or Objects.requireNonNullElseGet) keeps this from breaking if a legacy/deserialized block ever yields null.
| * @return The suggested rules, or an empty list when none were suggested | ||
| */ | ||
| @JsonProperty("suggested_rules") | ||
| public List<PermissionRule> getSuggestedRules() { |
There was a problem hiding this comment.
Two small things: (1) the name pendToolMap reads as key→tool while it is key→rules — pendingSuggestedRules would match the new getter; (2) getSuggestedRules() documents "empty list when none were suggested", so the caller-side assertNotNull in ReActAgentHitlTest tests the contract of a method that cannot return null — asserting the context block (not only the RequireUserConfirmEvent payload) carries the rules would cover the write-back path in updateToolCallStatesAndRules, which is where the [Warning] above lives.
…k-suggested-rules-final # Conflicts: # agentscope-core/src/main/java/io/agentscope/core/ReActAgent.java # agentscope-core/src/main/java/io/agentscope/core/message/ToolUseBlock.java # agentscope-core/src/test/java/io/agentscope/core/agent/ReActAgentHitlTest.java
|
fixed in: #3293 |
实现了 ToolUseBlock.getSuggestedRules() 的完整功能,该功能已在文档中描述但尚未实现。
变更内容
关闭: #1592
核心实现
测试
示例
测试
已通过的测试
mvn test -Dtest=ToolUseBlockTest
mvn spotless:check
所有新功能都包含了单元测试,代码格式符合项目规范。
相关 Issue
此 PR 实现了已在 javadoc 中承诺但缺失的功能,修复了代码与文档不一致的问题。
检查清单