fix(tool): implement tool_type filter in ToolRegistry.list_tools() #3308
@@ -966,9 +966,50 @@ Feature: Consolidated Tool
|
||||
# ---- List Tools With tool_type Filter ----
|
||||
|
||||
|
||||
Scenario: List tools with tool_type filter
|
||||
Scenario: List tools with tool_type filter returns matching tools
|
||||
Given a tool registry
|
||||
And a registered tool spec named "test/filtered"
|
||||
And a registered tool spec named "test/filtered" with tool_type "tool"
|
||||
When I list tools with tool_type "tool"
|
||||
Then the tool list should contain 1 tools
|
||||
|
||||
Scenario: List tools with tool_type validation returns only validations
|
||||
Given a tool registry
|
||||
And a registered tool spec named "test/my-tool" with tool_type "tool"
|
||||
And a registered tool spec named "test/my-validation" with tool_type "validation"
|
||||
When I list tools with tool_type "validation"
|
||||
Then the tool list should contain 1 tools
|
||||
|
||||
Scenario: List tools with tool_type tool excludes validations
|
||||
Given a tool registry
|
||||
And a registered tool spec named "test/plain-tool" with tool_type "tool"
|
||||
And a registered tool spec named "test/plain-validation" with tool_type "validation"
|
||||
When I list tools with tool_type "tool"
|
||||
Then the tool list should contain 1 tools
|
||||
|
||||
Scenario: List tools without tool_type filter returns all tools
|
||||
Given a tool registry
|
||||
And a registered tool spec named "test/all-tool" with tool_type "tool"
|
||||
And a registered tool spec named "test/all-validation" with tool_type "validation"
|
||||
When I list all tools
|
||||
Then the tool list should contain 2 tools
|
||||
|
||||
Scenario: List tools with tool_type None returns all tools
|
||||
Given a tool registry
|
||||
And a registered tool spec named "test/none-tool" with tool_type "tool"
|
||||
And a registered tool spec named "test/none-validation" with tool_type "validation"
|
||||
When I list all tools
|
||||
Then the tool list should contain 2 tools
|
||||
|
||||
Scenario: ToolSpec default tool_type is tool
|
||||
Given a tool registry
|
||||
And a registered tool spec named "test/default-type"
|
||||
When I list tools with tool_type "tool"
|
||||
Then the tool list should contain 1 tools
|
||||
|
||||
Scenario: ToolSpec with validation tool_type is excluded from tool filter
|
||||
Given a tool registry
|
||||
And a registered tool spec named "test/excl-validation" with tool_type "validation"
|
||||
When I list tools with tool_type "tool"
|
||||
Then the tool list should contain 0 tools
|
||||
|
||||
|
||||
|
||||
@@ -68,6 +68,17 @@ def step_given_registered_tool_spec(context: Any, name: str) -> None:
|
||||
context.registry.register(spec)
|
||||
|
||||
|
||||
@given('a registered tool spec named "{name}" with tool_type "{tt}"')
|
||||
def step_given_registered_tool_spec_with_type(context: Any, name: str, tt: str) -> None:
|
||||
spec = ToolSpec(
|
||||
name=name,
|
||||
description=f"Test tool {name}",
|
||||
handler=_echo_handler,
|
||||
tool_type=tt,
|
||||
)
|
||||
context.registry.register(spec)
|
||||
|
||||
|
||||
@given('a registered tool spec named "{name}" with an adder handler')
|
||||
def step_given_registered_adder(context: Any, name: str) -> None:
|
||||
spec = ToolSpec(
|
||||
|
||||
@@ -193,9 +193,43 @@ def validation_detach() -> None:
|
||||
print("validation-cli-detach-ok")
|
||||
|
||||
|
||||
def tool_list_type_tool() -> None:
|
||||
"""Verify tool list --type tool returns only tools."""
|
||||
svc = MagicMock()
|
||||
svc.list_tools.return_value = [_mock_tool("local/smoke-tool", "tool")]
|
||||
with patch(
|
||||
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
||||
return_value=svc,
|
||||
):
|
||||
result = runner.invoke(tool_app, ["list", "--type", "tool"])
|
||||
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
||||
svc.list_tools.assert_called_once_with(
|
||||
namespace=None, tool_type="tool", source=None
|
||||
)
|
||||
print("tool-cli-list-type-tool-ok")
|
||||
|
||||
|
||||
def tool_list_type_validation() -> None:
|
||||
"""Verify tool list --type validation returns only validations."""
|
||||
svc = MagicMock()
|
||||
svc.list_tools.return_value = [_mock_tool("local/smoke-val", "validation")]
|
||||
with patch(
|
||||
"cleveragents.cli.commands.tool._get_tool_registry_service",
|
||||
return_value=svc,
|
||||
):
|
||||
result = runner.invoke(tool_app, ["list", "--type", "validation"])
|
||||
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
|
||||
svc.list_tools.assert_called_once_with(
|
||||
namespace=None, tool_type="validation", source=None
|
||||
)
|
||||
print("tool-cli-list-type-validation-ok")
|
||||
|
||||
|
||||
_COMMANDS = {
|
||||
"tool-add-config": tool_add_config,
|
||||
"tool-list": tool_list_all,
|
||||
"tool-list-type-tool": tool_list_type_tool,
|
||||
"tool-list-type-validation": tool_list_type_validation,
|
||||
"tool-show": tool_show_name,
|
||||
"tool-remove": tool_remove_name,
|
||||
"validation-add-config": validation_add_config,
|
||||
|
||||
@@ -55,3 +55,19 @@ Validation Detach Removes Attachment
|
||||
${result}= Run Process ${PYTHON} ${HELPER} validation-detach cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} validation-cli-detach-ok
|
||||
|
||||
Tool List Type Tool Filter Works
|
||||
[Documentation] Verify that ``tool list --type tool`` passes tool_type to service
|
||||
${result}= Run Process ${PYTHON} ${HELPER} tool-list-type-tool cwd=${WORKSPACE}
|
||||
Log ${result.stdout}
|
||||
Log ${result.stderr}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} tool-cli-list-type-tool-ok
|
||||
|
||||
Tool List Type Validation Filter Works
|
||||
[Documentation] Verify that ``tool list --type validation`` passes tool_type to service
|
||||
${result}= Run Process ${PYTHON} ${HELPER} tool-list-type-validation cwd=${WORKSPACE}
|
||||
Log ${result.stdout}
|
||||
Log ${result.stderr}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} tool-cli-list-type-validation-ok
|
||||
|
||||
@@ -75,8 +75,9 @@ class ToolRegistry:
|
||||
If provided, only return tools whose name starts with
|
||||
``namespace/``.
|
||||
tool_type:
|
||||
Reserved for future use (e.g. filtering by ``tool`` vs
|
||||
``validation``). Currently unused.
|
||||
If provided, only return tools whose ``tool_type`` matches
|
||||
(``"tool"`` or ``"validation"``). When ``None``, all tools
|
||||
are returned regardless of type.
|
||||
source:
|
||||
If provided, only return tools whose ``source`` field matches.
|
||||
"""
|
||||
@@ -90,9 +91,8 @@ class ToolRegistry:
|
||||
if source is not None:
|
||||
specs = [s for s in specs if s.source == source]
|
||||
|
||||
# tool_type filtering is a no-op for now but the parameter is
|
||||
# part of the public API to avoid a breaking change later.
|
||||
_ = tool_type
|
||||
if tool_type is not None:
|
||||
specs = [s for s in specs if s.tool_type == tool_type]
|
||||
|
||||
return specs
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Provides the execution-layer data structures that sit on top of the domain
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
@@ -82,6 +82,10 @@ class ToolSpec(BaseModel):
|
||||
...,
|
||||
description="Callable that executes the tool logic",
|
||||
)
|
||||
tool_type: Literal["tool", "validation"] = Field(
|
||||
default="tool",
|
||||
description="Discriminator: tool or validation",
|
||||
)
|
||||
source: str = Field(
|
||||
default="builtin",
|
||||
description=("Origin of the tool: builtin, agent_skills, mcp, or custom"),
|
||||
|
||||
Reference in New Issue
Block a user