feat: add tool call release decisions#6165
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughIntroduces a ChangesToolCallDecision release-control mediation
Sequence Diagram(s)sequenceDiagram
participant Agent as AgentExecutor
participant Hook as BeforeToolCallHook
participant Resolver as resolve_tool_call_decision
participant Decision as ToolCallDecision
participant Tool as ToolFunction
Agent->>Hook: call(ToolCallHookContext)
Hook-->>Agent: ToolCallDecision | bool | None
Agent->>Resolver: resolve_tool_call_decision(hook_result, tool_name)
alt result is ToolCallDecision
Resolver->>Decision: should_execute?
alt PROCEED
Decision-->>Resolver: True
Resolver-->>Agent: None
Agent->>Tool: execute()
else NEEDS_REVIEW or SILENCE
Decision-->>Resolver: False
Resolver->>Decision: block_message(tool_name)
Decision-->>Resolver: formatted block message
Resolver-->>Agent: block message (str)
Agent-->>Agent: result = block message (skip tool)
end
else result is False
Resolver-->>Agent: generic block message
Agent-->>Agent: result = block message (skip tool)
else result is None or True
Resolver-->>Agent: None
Agent->>Tool: execute()
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Review-fix update:
Remaining external blocker: repository GitHub Actions are still |
|
Tip For best results, initiate chat on the files or code changes.
For the GitHub Actions blocker, you may want to ping a maintainer directly (e.g., via a comment tagging them) to request approval of the workflow runs. Once CI passes, the PR should be in a good state for merge consideration. Is there anything else you'd like me to look into or review on this PR? |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
lib/crewai/src/crewai/hooks/tool_call_decision.py (1)
41-42: ⚡ Quick win
frozen=Trueis undermined by aliasing mutablereview_context.On Line 41 / Line 54 / Line 67, non-empty dicts are stored by reference. External mutation after creation can still change decision payloads.
Suggested patch
`@dataclass`(frozen=True) class ToolCallDecision: @@ + `@staticmethod` + def _copy_context( + review_context: dict[str, Any] | None, + ) -> dict[str, Any]: + return dict(review_context) if review_context is not None else {} + `@classmethod` def proceed( @@ return cls( decision=ToolCallDecisionType.PROCEED, reason=reason, - review_context=review_context or {}, + review_context=cls._copy_context(review_context), ) @@ return cls( decision=ToolCallDecisionType.NEEDS_REVIEW, reason=reason, - review_context=review_context or {}, + review_context=cls._copy_context(review_context), ) @@ return cls( decision=ToolCallDecisionType.SILENCE, reason=reason, - review_context=review_context or {}, + review_context=cls._copy_context(review_context), )Also applies to: 54-55, 67-68
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai/src/crewai/hooks/tool_call_decision.py` around lines 41 - 42, The frozen=True dataclass is storing mutable dict objects by reference, allowing external code to mutate the review_context after object creation despite the frozen constraint. To fix this, create defensive copies of the review_context dict in all three locations where it appears (at lines 41-42, 54-55, and 67-68) by replacing review_context or {} with dict(review_context or {}) to ensure a copy is stored rather than a reference to the original mutable object.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/v1.13.0/en/learn/tool-hooks.mdx`:
- Around line 28-55: The file docs/v1.13.0/en/learn/tool-hooks.mdx is part of a
frozen version snapshot documentation and should not be modified. Move all the
changes you made to this file (the before_hook function documentation and the
release_control example with ToolCallDecision) to the editable source
documentation location instead, typically found in the main docs folder without
the version number prefix (docs/en/learn/tool-hooks.mdx or similar). Keep the
docs/v1.13.0/ snapshot unchanged unless this is explicitly a [docs-freeze]
release-cut PR.
In `@lib/crewai/src/crewai/hooks/tool_hooks.py`:
- Around line 120-133: The function resolve_tool_call_decision currently fails
open by returning None at the end if the hook_result does not match any expected
types (ToolCallDecision instance or boolean False), which silently allows tool
execution for unexpected return values. Replace the final return None statement
with a blocking response that raises an exception or returns a block message
indicating an invalid hook return type, ensuring that any unexpected hook return
values fail closed by preventing tool execution rather than allowing it.
---
Nitpick comments:
In `@lib/crewai/src/crewai/hooks/tool_call_decision.py`:
- Around line 41-42: The frozen=True dataclass is storing mutable dict objects
by reference, allowing external code to mutate the review_context after object
creation despite the frozen constraint. To fix this, create defensive copies of
the review_context dict in all three locations where it appears (at lines 41-42,
54-55, and 67-68) by replacing review_context or {} with dict(review_context or
{}) to ensure a copy is stored rather than a reference to the original mutable
object.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 950aee1b-78de-4cdd-8efd-26750a092098
📒 Files selected for processing (12)
docs/v1.13.0/en/learn/tool-hooks.mdxlib/crewai/src/crewai/agents/crew_agent_executor.pylib/crewai/src/crewai/experimental/agent_executor.pylib/crewai/src/crewai/hooks/__init__.pylib/crewai/src/crewai/hooks/decorators.pylib/crewai/src/crewai/hooks/tool_call_decision.pylib/crewai/src/crewai/hooks/tool_hooks.pylib/crewai/src/crewai/hooks/types.pylib/crewai/src/crewai/hooks/wrappers.pylib/crewai/src/crewai/utilities/agent_utils.pylib/crewai/src/crewai/utilities/tool_utils.pylib/crewai/tests/hooks/test_tool_hooks.py
| def before_hook(context: ToolCallHookContext) -> bool | ToolCallDecision | None: | ||
| # Return False to block execution with a generic message | ||
| # Return ToolCallDecision.needs_review(...) to pause for review | ||
| # Return ToolCallDecision.silence(...) to suppress execution | ||
| # Return True, None, or ToolCallDecision.proceed(...) to allow execution | ||
| ... | ||
| ``` | ||
|
|
||
| For runtime release-control, return a `ToolCallDecision` from a before-tool hook. | ||
| This lets a hook distinguish normal execution from calls that need review or should | ||
| be silenced before the tool runs: | ||
|
|
||
| ```python | ||
| from crewai.hooks import ToolCallDecision, ToolCallHookContext, before_tool_call | ||
|
|
||
| @before_tool_call | ||
| def release_control(context: ToolCallHookContext) -> ToolCallDecision | None: | ||
| if context.tool_name == "send_email": | ||
| return ToolCallDecision.needs_review( | ||
| reason="Outbound email requires human approval", | ||
| review_context={"input": context.tool_input}, | ||
| ) | ||
|
|
||
| if context.tool_name == "post_to_social_media": | ||
| return ToolCallDecision.silence(reason="Public posting is disabled") | ||
|
|
||
| return ToolCallDecision.proceed() | ||
| ``` |
There was a problem hiding this comment.
Do not modify frozen snapshot docs under docs/v*.
This file is under a versioned frozen-docs path. Move this update to the editable docs source and keep this snapshot unchanged (unless this is explicitly a [docs-freeze] release-cut PR).
As per coding guidelines, docs/v[0-9]*.[0-9]*.[0-9]*/**: Never modify files under docs/v*/ as they are frozen release snapshots, except in [docs-freeze] release-cut PRs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/v1.13.0/en/learn/tool-hooks.mdx` around lines 28 - 55, The file
docs/v1.13.0/en/learn/tool-hooks.mdx is part of a frozen version snapshot
documentation and should not be modified. Move all the changes you made to this
file (the before_hook function documentation and the release_control example
with ToolCallDecision) to the editable source documentation location instead,
typically found in the main docs folder without the version number prefix
(docs/en/learn/tool-hooks.mdx or similar). Keep the docs/v1.13.0/ snapshot
unchanged unless this is explicitly a [docs-freeze] release-cut PR.
Source: Coding guidelines
….com/maxpetrusenkoagent/crewAI into hermes/oss-pr-2026-06-15-crewai-6025
…2026-06-18-crewai-6165
|
Review-fix sweep update:
Verification run locally:
Confidence: high for the review-fix scope. Remaining external state may still depend on repository Actions approval for forked PR workflows. |
Summary
ToolCallDecisiontri-state release-control results for before-tool hooksCloses #6025
Test Plan
uv run pytest lib/crewai/tests/hooks/test_tool_hooks.py::TestToolHooksIntegration::test_needs_review_decision_blocks_react_tool_execution lib/crewai/tests/hooks/test_tool_hooks.py::TestToolHooksIntegration::test_tool_call_decision_factories -quv run pytest lib/crewai/tests/hooks/test_tool_hooks.py -quv run ruff check --diff lib/crewai/src/crewai/hooks/tool_call_decision.py lib/crewai/src/crewai/hooks/tool_hooks.py lib/crewai/src/crewai/hooks/types.py lib/crewai/src/crewai/hooks/decorators.py lib/crewai/src/crewai/hooks/wrappers.py lib/crewai/src/crewai/agents/crew_agent_executor.py lib/crewai/src/crewai/experimental/agent_executor.py lib/crewai/src/crewai/utilities/agent_utils.py lib/crewai/src/crewai/utilities/tool_utils.pyuv run ruff format --check lib/crewai/src/crewai/hooks/tool_call_decision.py lib/crewai/src/crewai/hooks/tool_hooks.py lib/crewai/src/crewai/hooks/types.py lib/crewai/src/crewai/hooks/decorators.py lib/crewai/src/crewai/hooks/wrappers.py lib/crewai/src/crewai/agents/crew_agent_executor.py lib/crewai/src/crewai/experimental/agent_executor.py lib/crewai/src/crewai/utilities/agent_utils.py lib/crewai/src/crewai/utilities/tool_utils.py lib/crewai/tests/hooks/test_tool_hooks.pygit diff --checkSummary by CodeRabbit
New Features
ToolCallDecisionfor release control, enabling PROCEED, NEEDS_REVIEW, or SILENCE decisions with optional reasons and review context.Refactor
Tests
Documentation