fix: skip tool hooks for framework-internal tools#939
fix: skip tool hooks for framework-internal tools#939araujof wants to merge 6 commits intogenerative-computing:mainfrom
Conversation
|
The PR description has been updated. Please fill out the template for your PR to be reviewed. |
psschwei
left a comment
There was a problem hiding this comment.
couple of minor nits, but otherwise looks ok to me
|
|
||
| # Framework-internal tool names that bypass plugin hooks by default. | ||
| # See mellea.stdlib.components.react.MELLEA_FINALIZER_TOOL | ||
| _INTERNAL_TOOL_NAMES: frozenset[str] = frozenset({"final_answer"}) |
There was a problem hiding this comment.
should we use the constant instead of hardcoding?
There was a problem hiding this comment.
same note - MELLEA_FINALIZER_TOOL
ie
from mellea.stdlib.components.react import MELLEA_FINALIZER_TOOL_INTERNAL_TOOL_NAMES: frozenset[str] = frozenset({MELLEA_FINALIZER_TOOL})There was a problem hiding this comment.
Sounds good. This doesn't create a circular import, right?
There was a problem hiding this comment.
Circular import confirmed. Despite react.py not directly importing mellea.plugins, the chain goes through mellea.stdlib.components.__init__ which imports from mellea.core, creating: core.backend → plugins.manager → stdlib.components.react → stdlib.components.__init__ → core (partially initialized). So the MELLEA_FINALIZER_TOOL import cannot be added to manager.py.. One option would be to refactor constant names to an external shared module, but maybe this is too much for this fix (given we only have one internal tool defined. I added a comment to reference the finalizer tool.
There was a problem hiding this comment.
Agreed - I'd skip it here. could optionally openup a followup issue to track
|
I will defer to Nathan's comment here: #921 (comment) At the very least, I don't think we should have these be a part of Mellea core: Even if we set an internal flag on some mellea tools, we should let the plugin decide explicitly whether to skip them. I also have some additional comments here that I think are applicable: #921 (review) |
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> Co-authored-by: Paul Schweigert <paul@paulschweigert.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> Co-authored-by: Paul Schweigert <paul@paulschweigert.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Thanks @jakelorocco ! Great suggestion. I refactored the PR to enable per-plugin control-flow tool filtering. I also updated the examples and docs to show how it works. What changedReplaced the global WhyThe global flag hid tool calls from every plugin (including logging/telemetry plugins that need to see them). Nathan and Jake's feedback: the plugin should decide, not the framework. Key changes
Recommended plugin pattern@hook(HookType.TOOL_PRE_INVOKE, mode=PluginMode.CONCURRENT, priority=5)
async def enforce_tool_allowlist(payload, _):
if payload.is_control_flow:
return # framework control-flow tools are exempt
if payload.model_tool_call.name not in ALLOWED_TOOLS:
return block(f"Tool '{payload.model_tool_call.name}' not permitted")Logging plugins that want to observe all tools simply ignore the flag. |
Misc PR
Type of PR
Description
final_answertool #938Summary
tool_pre_invoke/tool_post_invokehooks are automatically skipped for framework-internal tools likefinal_answerset_skip_hooks_for_internal_tools(False)Changes
mellea/plugins/manager.py_INTERNAL_TOOL_NAMESfrozenset ({"final_answer"}) and_skip_hooks_for_internal_toolsboolean (defaultTrue)skip_hooks_for_internal_tools(),set_skip_hooks_for_internal_tools(),is_internal_tool()shutdown_plugins()resets the flag toTruefor test isolationmellea/stdlib/functional.py_acall_tools(), gate bothTOOL_PRE_INVOKEandTOOL_POST_INVOKEblocks with a_run_hookscheck that skips internal tools when the flag is enabledmellea/plugins/__init__.pydocs/docs/concepts/plugins.mdxtool_pre_invokereferencing the exemptiontest/plugins/test_internal_tool_hook_skip.py(new)is_internal_toolunit test, shutdown resets flagTesting
Test plan
uv run pytest test/plugins/test_internal_tool_hook_skip.py -v— all new tests passuv run pytest test/plugins/test_tool_hooks_redaction.py -v— no regressionuv run pytest test/stdlib/frameworks/test_react_framework.py -v— no regressionuv run ruff check/ruff format— cleanuv run mypy mellea/plugins/manager.py mellea/stdlib/functional.py— no issuesAttribution
Related
Part of #920.