Merge branch 'lorenze/imp/native-tool-calling' of github.com:crewAIInc/crewAI into lorenze/imp/native-tool-calling

This commit is contained in:
lorenzejay
2026-01-22 16:53:16 -08:00
13 changed files with 128 additions and 107 deletions

View File

@@ -89,6 +89,7 @@ from crewai.utilities.guardrail_types import GuardrailType
from crewai.utilities.llm_utils import create_llm
from crewai.utilities.prompts import Prompts, StandardPromptResult, SystemPromptResult
from crewai.utilities.pydantic_schema_utils import generate_model_description
from crewai.utilities.string_utils import sanitize_tool_name
from crewai.utilities.token_counter_callback import TokenCalcHandler
from crewai.utilities.training_handler import CrewTrainingHandler
@@ -1339,10 +1340,10 @@ class Agent(BaseAgent):
args_schema = None
if hasattr(tool, "inputSchema") and tool.inputSchema:
args_schema = self._json_schema_to_pydantic(
tool.name, tool.inputSchema
sanitize_tool_name(tool.name), tool.inputSchema
)
schemas[tool.name] = {
schemas[sanitize_tool_name(tool.name)] = {
"description": getattr(tool, "description", ""),
"args_schema": args_schema,
}
@@ -1498,7 +1499,7 @@ class Agent(BaseAgent):
"""
return "\n".join(
[
f"Tool name: {tool.name}\nTool description:\n{tool.description}"
f"Tool name: {sanitize_tool_name(tool.name)}\nTool description:\n{tool.description}"
for tool in tools
]
)

View File

@@ -104,6 +104,7 @@ from crewai.utilities.streaming import (
signal_end,
signal_error,
)
from crewai.utilities.string_utils import sanitize_tool_name
from crewai.utilities.task_output_storage_handler import TaskOutputStorageHandler
from crewai.utilities.training_handler import CrewTrainingHandler
@@ -1241,10 +1242,14 @@ class Crew(FlowTrackable, BaseModel):
return existing_tools
# Create mapping of tool names to new tools
new_tool_map = {tool.name: tool for tool in new_tools}
new_tool_map = {sanitize_tool_name(tool.name): tool for tool in new_tools}
# Remove any existing tools that will be replaced
tools = [tool for tool in existing_tools if tool.name not in new_tool_map]
tools = [
tool
for tool in existing_tools
if sanitize_tool_name(tool.name) not in new_tool_map
]
# Add all new tools
tools.extend(new_tools)

View File

@@ -11,6 +11,7 @@ from crewai.experimental.evaluation.base_evaluator import (
)
from crewai.experimental.evaluation.json_parser import extract_json_from_llm_response
from crewai.task import Task
from crewai.utilities.string_utils import sanitize_tool_name
from crewai.utilities.types import LLMMessage
@@ -52,7 +53,9 @@ class ToolSelectionEvaluator(BaseEvaluator):
available_tools_info = ""
if agent.tools:
for tool in agent.tools:
available_tools_info += f"- {tool.name}: {tool.description}\n"
available_tools_info += (
f"- {sanitize_tool_name(tool.name)}: {tool.description}\n"
)
else:
available_tools_info = "No tools available"

View File

@@ -31,6 +31,7 @@ from crewai.mcp.transports.base import BaseTransport
from crewai.mcp.transports.http import HTTPTransport
from crewai.mcp.transports.sse import SSETransport
from crewai.mcp.transports.stdio import StdioTransport
from crewai.utilities.string_utils import sanitize_tool_name
# MCP Connection timeout constants (in seconds)
@@ -418,7 +419,7 @@ class MCPClient:
return [
{
"name": tool.name,
"name": sanitize_tool_name(tool.name),
"description": getattr(tool, "description", ""),
"inputSchema": getattr(tool, "inputSchema", {}),
}

View File

@@ -52,6 +52,7 @@ from crewai.telemetry.utils import (
close_span,
)
from crewai.utilities.logger_utils import suppress_warnings
from crewai.utilities.string_utils import sanitize_tool_name
logger = logging.getLogger(__name__)
@@ -323,7 +324,8 @@ class Telemetry:
),
"max_retry_limit": getattr(agent, "max_retry_limit", 3),
"tools_names": [
tool.name.casefold() for tool in agent.tools or []
sanitize_tool_name(tool.name)
for tool in agent.tools or []
],
# Add agent fingerprint data if sharing crew details
"fingerprint": (
@@ -372,7 +374,8 @@ class Telemetry:
else None
),
"tools_names": [
tool.name.casefold() for tool in task.tools or []
sanitize_tool_name(tool.name)
for tool in task.tools or []
],
# Add task fingerprint data if sharing crew details
"fingerprint": (
@@ -425,7 +428,8 @@ class Telemetry:
),
"max_retry_limit": getattr(agent, "max_retry_limit", 3),
"tools_names": [
tool.name.casefold() for tool in agent.tools or []
sanitize_tool_name(tool.name)
for tool in agent.tools or []
],
}
for agent in crew.agents
@@ -447,7 +451,8 @@ class Telemetry:
),
"agent_key": task.agent.key if task.agent else None,
"tools_names": [
tool.name.casefold() for tool in task.tools or []
sanitize_tool_name(tool.name)
for tool in task.tools or []
],
}
for task in crew.tasks
@@ -832,7 +837,8 @@ class Telemetry:
"llm": agent.llm.model,
"delegation_enabled?": agent.allow_delegation,
"tools_names": [
tool.name.casefold() for tool in agent.tools or []
sanitize_tool_name(tool.name)
for tool in agent.tools or []
],
}
for agent in crew.agents
@@ -858,7 +864,8 @@ class Telemetry:
else None
),
"tools_names": [
tool.name.casefold() for tool in task.tools or []
sanitize_tool_name(tool.name)
for tool in task.tools or []
],
}
for task in crew.tasks

View File

@@ -26,6 +26,7 @@ from typing_extensions import TypeIs
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.utilities.printer import Printer
from crewai.utilities.pydantic_schema_utils import generate_model_description
from crewai.utilities.string_utils import sanitize_tool_name
_printer = Printer()
@@ -259,10 +260,12 @@ class BaseTool(BaseModel, ABC):
else:
fields[name] = (param_annotation, param.default)
if fields:
args_schema = create_model(f"{tool.name}Input", **fields)
args_schema = create_model(
f"{sanitize_tool_name(tool.name)}_input", **fields
)
else:
args_schema = create_model(
f"{tool.name}Input", __base__=PydanticBaseModel
f"{sanitize_tool_name(tool.name)}_input", __base__=PydanticBaseModel
)
return cls(
@@ -301,7 +304,7 @@ class BaseTool(BaseModel, ABC):
schema = generate_model_description(self.args_schema)
args_json = json.dumps(schema["json_schema"]["schema"], indent=2)
self.description = (
f"Tool Name: {self.name}\n"
f"Tool Name: {sanitize_tool_name(self.name)}\n"
f"Tool Arguments: {args_json}\n"
f"Tool Description: {self.description}"
)
@@ -379,7 +382,7 @@ class Tool(BaseTool, Generic[P, R]):
if _is_awaitable(result):
return await result
raise NotImplementedError(
f"{self.name} does not have an async function. "
f"{sanitize_tool_name(self.name)} does not have an async function. "
"Use run() for sync execution or provide an async function."
)
@@ -421,10 +424,12 @@ class Tool(BaseTool, Generic[P, R]):
else:
fields[name] = (param_annotation, param.default)
if fields:
args_schema = create_model(f"{tool.name}Input", **fields)
args_schema = create_model(
f"{sanitize_tool_name(tool.name)}_input", **fields
)
else:
args_schema = create_model(
f"{tool.name}Input", __base__=PydanticBaseModel
f"{sanitize_tool_name(tool.name)}_input", __base__=PydanticBaseModel
)
return cls(

View File

@@ -2,6 +2,7 @@ from pydantic import BaseModel, Field
from crewai.agents.cache.cache_handler import CacheHandler
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.utilities.string_utils import sanitize_tool_name
class CacheTools(BaseModel):
@@ -13,14 +14,14 @@ class CacheTools(BaseModel):
default_factory=CacheHandler,
)
def tool(self):
def tool(self) -> CrewStructuredTool:
return CrewStructuredTool.from_function(
func=self.hit_cache,
name=self.name,
name=sanitize_tool_name(self.name),
description="Reads directly from the cache",
)
def hit_cache(self, key):
def hit_cache(self, key: str) -> str | None:
split = key.split("tool:")
tool = split[1].split("|input:")[0].strip()
tool_input = split[1].split("|input:")[1].strip()

View File

@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any, get_type_hints
from pydantic import BaseModel, Field, create_model
from crewai.utilities.logger import Logger
from crewai.utilities.string_utils import sanitize_tool_name
if TYPE_CHECKING:
@@ -229,7 +230,7 @@ class CrewStructuredTool:
if self.has_reached_max_usage_count():
raise ToolUsageLimitExceededError(
f"Tool '{self.name}' has reached its maximum usage limit of {self.max_usage_count}. You should not use the {self.name} tool again."
f"Tool '{sanitize_tool_name(self.name)}' has reached its maximum usage limit of {self.max_usage_count}. You should not use the {sanitize_tool_name(self.name)} tool again."
)
self._increment_usage_count()
@@ -261,7 +262,7 @@ class CrewStructuredTool:
if self.has_reached_max_usage_count():
raise ToolUsageLimitExceededError(
f"Tool '{self.name}' has reached its maximum usage limit of {self.max_usage_count}. You should not use the {self.name} tool again."
f"Tool '{sanitize_tool_name(self.name)}' has reached its maximum usage limit of {self.max_usage_count}. You should not use the {sanitize_tool_name(self.name)} tool again."
)
self._increment_usage_count()

View File

@@ -30,6 +30,7 @@ from crewai.utilities.agent_utils import (
from crewai.utilities.converter import Converter
from crewai.utilities.i18n import I18N, get_i18n
from crewai.utilities.printer import Printer
from crewai.utilities.string_utils import sanitize_tool_name
if TYPE_CHECKING:
@@ -145,7 +146,8 @@ class ToolUsage:
if (
isinstance(tool, CrewStructuredTool)
and tool.name == self._i18n.tools("add_image")["name"] # type: ignore
and sanitize_tool_name(tool.name)
== sanitize_tool_name(self._i18n.tools("add_image")["name"]) # type: ignore
):
try:
return self._use(tool_string=tool_string, tool=tool, calling=calling)
@@ -192,7 +194,8 @@ class ToolUsage:
if (
isinstance(tool, CrewStructuredTool)
and tool.name == self._i18n.tools("add_image")["name"] # type: ignore
and sanitize_tool_name(tool.name)
== sanitize_tool_name(self._i18n.tools("add_image")["name"]) # type: ignore
):
try:
return await self._ause(
@@ -233,7 +236,7 @@ class ToolUsage:
)
self._telemetry.tool_repeated_usage(
llm=self.function_calling_llm,
tool_name=tool.name,
tool_name=sanitize_tool_name(tool.name),
attempts=self._run_attempts,
)
return self._format_result(result=result)
@@ -278,7 +281,7 @@ class ToolUsage:
input_str = str(calling.arguments)
result = self.tools_handler.cache.read(
tool=calling.tool_name, input=input_str
tool=sanitize_tool_name(calling.tool_name), input=input_str
) # type: ignore
from_cache = result is not None
@@ -286,12 +289,15 @@ class ToolUsage:
(
available_tool
for available_tool in self.tools
if available_tool.name == tool.name
if sanitize_tool_name(available_tool.name)
== sanitize_tool_name(tool.name)
),
None,
)
usage_limit_error = self._check_usage_limit(available_tool, tool.name)
usage_limit_error = self._check_usage_limit(
available_tool, sanitize_tool_name(tool.name)
)
if usage_limit_error:
result = usage_limit_error
self._telemetry.tool_usage_error(llm=self.function_calling_llm)
@@ -299,9 +305,9 @@ class ToolUsage:
# Don't return early - fall through to finally block
elif result is None:
try:
if calling.tool_name in [
"Delegate work to coworker",
"Ask question to coworker",
if sanitize_tool_name(calling.tool_name) in [
sanitize_tool_name("Delegate work to coworker"),
sanitize_tool_name("Ask question to coworker"),
]:
coworker = (
calling.arguments.get("coworker")
@@ -350,13 +356,13 @@ class ToolUsage:
self._telemetry.tool_usage(
llm=self.function_calling_llm,
tool_name=tool.name,
tool_name=sanitize_tool_name(tool.name),
attempts=self._run_attempts,
)
result = self._format_result(result=result)
data = {
"result": result,
"tool_name": tool.name,
"tool_name": sanitize_tool_name(tool.name),
"tool_args": calling.arguments,
}
@@ -380,7 +386,7 @@ class ToolUsage:
and available_tool.max_usage_count is not None
):
self._printer.print(
content=f"Tool '{available_tool.name}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
color="blue",
)
elif available_tool and hasattr(
@@ -392,7 +398,7 @@ class ToolUsage:
and available_tool.max_usage_count is not None
):
self._printer.print(
content=f"Tool '{available_tool.name}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
color="blue",
)
@@ -403,7 +409,11 @@ class ToolUsage:
self._telemetry.tool_usage_error(llm=self.function_calling_llm)
error_message = self._i18n.errors(
"tool_usage_exception"
).format(error=e, tool=tool.name, tool_inputs=tool.description)
).format(
error=e,
tool=sanitize_tool_name(tool.name),
tool_inputs=tool.description,
)
result = ToolUsageError(
f"\n{error_message}.\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}"
).message
@@ -450,7 +460,7 @@ class ToolUsage:
)
self._telemetry.tool_repeated_usage(
llm=self.function_calling_llm,
tool_name=tool.name,
tool_name=sanitize_tool_name(tool.name),
attempts=self._run_attempts,
)
return self._format_result(result=result)
@@ -497,7 +507,7 @@ class ToolUsage:
input_str = str(calling.arguments)
result = self.tools_handler.cache.read(
tool=calling.tool_name, input=input_str
tool=sanitize_tool_name(calling.tool_name), input=input_str
) # type: ignore
from_cache = result is not None
@@ -505,12 +515,15 @@ class ToolUsage:
(
available_tool
for available_tool in self.tools
if available_tool.name == tool.name
if sanitize_tool_name(available_tool.name)
== sanitize_tool_name(tool.name)
),
None,
)
usage_limit_error = self._check_usage_limit(available_tool, tool.name)
usage_limit_error = self._check_usage_limit(
available_tool, sanitize_tool_name(tool.name)
)
if usage_limit_error:
result = usage_limit_error
self._telemetry.tool_usage_error(llm=self.function_calling_llm)
@@ -518,9 +531,9 @@ class ToolUsage:
# Don't return early - fall through to finally block
elif result is None:
try:
if calling.tool_name in [
"Delegate work to coworker",
"Ask question to coworker",
if sanitize_tool_name(calling.tool_name) in [
sanitize_tool_name("Delegate work to coworker"),
sanitize_tool_name("Ask question to coworker"),
]:
coworker = (
calling.arguments.get("coworker")
@@ -569,13 +582,13 @@ class ToolUsage:
self._telemetry.tool_usage(
llm=self.function_calling_llm,
tool_name=tool.name,
tool_name=sanitize_tool_name(tool.name),
attempts=self._run_attempts,
)
result = self._format_result(result=result)
data = {
"result": result,
"tool_name": tool.name,
"tool_name": sanitize_tool_name(tool.name),
"tool_args": calling.arguments,
}
@@ -599,7 +612,7 @@ class ToolUsage:
and available_tool.max_usage_count is not None
):
self._printer.print(
content=f"Tool '{available_tool.name}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
color="blue",
)
elif available_tool and hasattr(
@@ -611,7 +624,7 @@ class ToolUsage:
and available_tool.max_usage_count is not None
):
self._printer.print(
content=f"Tool '{available_tool.name}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
content=f"Tool '{sanitize_tool_name(available_tool.name)}' usage: {available_tool.current_usage_count}/{available_tool.max_usage_count}",
color="blue",
)
@@ -622,7 +635,11 @@ class ToolUsage:
self._telemetry.tool_usage_error(llm=self.function_calling_llm)
error_message = self._i18n.errors(
"tool_usage_exception"
).format(error=e, tool=tool.name, tool_inputs=tool.description)
).format(
error=e,
tool=sanitize_tool_name(tool.name),
tool_inputs=tool.description,
)
result = ToolUsageError(
f"\n{error_message}.\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}"
).message
@@ -680,9 +697,10 @@ class ToolUsage:
if not self.tools_handler:
return False
if last_tool_usage := self.tools_handler.last_used_tool:
return (calling.tool_name == last_tool_usage.tool_name) and (
calling.arguments == last_tool_usage.arguments
)
return (
sanitize_tool_name(calling.tool_name)
== sanitize_tool_name(last_tool_usage.tool_name)
) and (calling.arguments == last_tool_usage.arguments)
return False
@staticmethod
@@ -705,20 +723,19 @@ class ToolUsage:
return None
def _select_tool(self, tool_name: str) -> Any:
sanitized_input = sanitize_tool_name(tool_name)
order_tools = sorted(
self.tools,
key=lambda tool: SequenceMatcher(
None, tool.name.lower().strip(), tool_name.lower().strip()
None, sanitize_tool_name(tool.name), sanitized_input
).ratio(),
reverse=True,
)
for tool in order_tools:
sanitized_tool = sanitize_tool_name(tool.name)
if (
tool.name.lower().strip() == tool_name.lower().strip()
or SequenceMatcher(
None, tool.name.lower().strip(), tool_name.lower().strip()
).ratio()
> 0.85
sanitized_tool == sanitized_input
or SequenceMatcher(None, sanitized_tool, sanitized_input).ratio() > 0.85
):
return tool
if self.task:
@@ -803,7 +820,7 @@ class ToolUsage:
return ToolUsageError(f"{self._i18n.errors('tool_arguments_error')}")
return ToolCalling(
tool_name=tool.name,
tool_name=sanitize_tool_name(tool.name),
arguments=arguments,
)
@@ -957,7 +974,7 @@ class ToolUsage:
event_data = {
"run_attempts": self._run_attempts,
"delegations": self.task.delegations if self.task else 0,
"tool_name": tool.name,
"tool_name": sanitize_tool_name(tool.name),
"tool_args": tool_calling.arguments,
"tool_class": tool.__class__.__name__,
"agent_key": (

View File

@@ -13,6 +13,7 @@ from crewai.events.types.reasoning_events import (
)
from crewai.llm import LLM
from crewai.task import Task
from crewai.utilities.string_utils import sanitize_tool_name
class ReasoningPlan(BaseModel):
@@ -340,7 +341,9 @@ class AgentReasoning:
str: Comma-separated list of tool names.
"""
try:
return ", ".join([tool.name for tool in (self.task.tools or [])])
return ", ".join(
[sanitize_tool_name(tool.name) for tool in (self.task.tools or [])]
)
except (AttributeError, TypeError):
return "No tools available"

View File

@@ -15,6 +15,7 @@ from crewai.tools.tool_types import ToolResult
from crewai.tools.tool_usage import ToolUsage, ToolUsageError
from crewai.utilities.i18n import I18N
from crewai.utilities.logger import Logger
from crewai.utilities.string_utils import sanitize_tool_name
if TYPE_CHECKING:
@@ -63,7 +64,7 @@ async def aexecute_tool_and_check_finality(
treated as a final answer.
"""
logger = Logger(verbose=crew.verbose if crew else False)
tool_name_to_tool_map = {tool.name: tool for tool in tools}
tool_name_to_tool_map = {sanitize_tool_name(tool.name): tool for tool in tools}
if agent_key and agent_role and agent:
fingerprint_context = fingerprint_context or {}
@@ -90,19 +91,9 @@ async def aexecute_tool_and_check_finality(
if isinstance(tool_calling, ToolUsageError):
return ToolResult(tool_calling.message, False)
if tool_calling.tool_name.casefold().strip() in [
name.casefold().strip() for name in tool_name_to_tool_map
] or tool_calling.tool_name.casefold().replace("_", " ") in [
name.casefold().strip() for name in tool_name_to_tool_map
]:
tool = tool_name_to_tool_map.get(tool_calling.tool_name)
if not tool:
tool_result = i18n.errors("wrong_tool_name").format(
tool=tool_calling.tool_name,
tools=", ".join([t.name.casefold() for t in tools]),
)
return ToolResult(result=tool_result, result_as_answer=False)
sanitized_tool_name = sanitize_tool_name(tool_calling.tool_name)
tool = tool_name_to_tool_map.get(sanitized_tool_name)
if tool:
tool_input = tool_calling.arguments if tool_calling.arguments else {}
hook_context = ToolCallHookContext(
tool_name=tool_calling.tool_name,
@@ -152,8 +143,8 @@ async def aexecute_tool_and_check_finality(
return ToolResult(modified_result, tool.result_as_answer)
tool_result = i18n.errors("wrong_tool_name").format(
tool=tool_calling.tool_name,
tools=", ".join([tool.name.casefold() for tool in tools]),
tool=sanitized_tool_name,
tools=", ".join(tool_name_to_tool_map.keys()),
)
return ToolResult(result=tool_result, result_as_answer=False)
@@ -193,7 +184,7 @@ def execute_tool_and_check_finality(
ToolResult containing the execution result and whether it should be treated as a final answer
"""
logger = Logger(verbose=crew.verbose if crew else False)
tool_name_to_tool_map = {tool.name: tool for tool in tools}
tool_name_to_tool_map = {sanitize_tool_name(tool.name): tool for tool in tools}
if agent_key and agent_role and agent:
fingerprint_context = fingerprint_context or {}
@@ -206,7 +197,6 @@ def execute_tool_and_check_finality(
except Exception as e:
raise ValueError(f"Failed to set fingerprint: {e}") from e
# Create tool usage instance
tool_usage = ToolUsage(
tools_handler=tools_handler,
tools=tools,
@@ -216,26 +206,14 @@ def execute_tool_and_check_finality(
action=agent_action,
)
# Parse tool calling
tool_calling = tool_usage.parse_tool_calling(agent_action.text)
if isinstance(tool_calling, ToolUsageError):
return ToolResult(tool_calling.message, False)
# Check if tool name matches
if tool_calling.tool_name.casefold().strip() in [
name.casefold().strip() for name in tool_name_to_tool_map
] or tool_calling.tool_name.casefold().replace("_", " ") in [
name.casefold().strip() for name in tool_name_to_tool_map
]:
tool = tool_name_to_tool_map.get(tool_calling.tool_name)
if not tool:
tool_result = i18n.errors("wrong_tool_name").format(
tool=tool_calling.tool_name,
tools=", ".join([t.name.casefold() for t in tools]),
)
return ToolResult(result=tool_result, result_as_answer=False)
sanitized_tool_name = sanitize_tool_name(tool_calling.tool_name)
tool = tool_name_to_tool_map.get(sanitized_tool_name)
if tool:
tool_input = tool_calling.arguments if tool_calling.arguments else {}
hook_context = ToolCallHookContext(
tool_name=tool_calling.tool_name,
@@ -285,9 +263,8 @@ def execute_tool_and_check_finality(
return ToolResult(modified_result, tool.result_as_answer)
# Handle invalid tool name
tool_result = i18n.errors("wrong_tool_name").format(
tool=tool_calling.tool_name,
tools=", ".join([tool.name.casefold() for tool in tools]),
tool=sanitized_tool_name,
tools=", ".join(tool_name_to_tool_map.keys()),
)
return ToolResult(result=tool_result, result_as_answer=False)

View File

@@ -17,7 +17,7 @@ def test_creating_a_tool_using_annotation():
# Assert all the right attributes were defined
assert my_tool.name == "Name of my tool"
assert "Tool Name: Name of my tool" in my_tool.description
assert "Tool Name: name_of_my_tool" in my_tool.description
assert "Tool Arguments:" in my_tool.description
assert '"question"' in my_tool.description
assert '"type": "string"' in my_tool.description
@@ -32,7 +32,7 @@ def test_creating_a_tool_using_annotation():
converted_tool = my_tool.to_structured_tool()
assert converted_tool.name == "Name of my tool"
assert "Tool Name: Name of my tool" in converted_tool.description
assert "Tool Name: name_of_my_tool" in converted_tool.description
assert "Tool Arguments:" in converted_tool.description
assert '"question"' in converted_tool.description
assert converted_tool.args_schema.model_json_schema()["properties"] == {
@@ -56,7 +56,7 @@ def test_creating_a_tool_using_baseclass():
# Assert all the right attributes were defined
assert my_tool.name == "Name of my tool"
assert "Tool Name: Name of my tool" in my_tool.description
assert "Tool Name: name_of_my_tool" in my_tool.description
assert "Tool Arguments:" in my_tool.description
assert '"question"' in my_tool.description
assert '"type": "string"' in my_tool.description
@@ -69,7 +69,7 @@ def test_creating_a_tool_using_baseclass():
converted_tool = my_tool.to_structured_tool()
assert converted_tool.name == "Name of my tool"
assert "Tool Name: Name of my tool" in converted_tool.description
assert "Tool Name: name_of_my_tool" in converted_tool.description
assert "Tool Arguments:" in converted_tool.description
assert '"question"' in converted_tool.description
assert converted_tool.args_schema.model_json_schema()["properties"] == {

View File

@@ -108,7 +108,7 @@ def test_tool_usage_render():
rendered = tool_usage._render()
# Check that the rendered output contains the expected tool information
assert "Tool Name: Random Number Generator" in rendered
assert "Tool Name: random_number_generator" in rendered
assert "Tool Arguments:" in rendered
assert (
"Tool Description: Generates a random number within a specified range"
@@ -488,7 +488,7 @@ def test_tool_selection_error_event_direct():
assert event.agent_role == "test_role"
assert event.tool_name == "Non Existent Tool"
assert event.tool_args == {}
assert "Tool Name: Test Tool" in event.tool_class
assert "Tool Name: test_tool" in event.tool_class
assert "A test tool" in event.tool_class
assert "don't exist" in event.error
@@ -503,7 +503,7 @@ def test_tool_selection_error_event_direct():
assert event.agent_role == "test_role"
assert event.tool_name == ""
assert event.tool_args == {}
assert "Test Tool" in event.tool_class
assert "test_tool" in event.tool_class
assert "forgot the Action name" in event.error
@@ -654,7 +654,7 @@ def test_tool_usage_finished_event_with_result():
# Verify event attributes
assert event.agent_key == "test_agent_key"
assert event.agent_role == "test_agent_role"
assert event.tool_name == "Test Tool"
assert event.tool_name == "test_tool"
assert event.tool_args == {"arg1": "value1"}
assert event.tool_class == "TestTool"
assert event.run_attempts == 1 # Default value from ToolUsage
@@ -734,7 +734,7 @@ def test_tool_usage_finished_event_with_cached_result():
# Verify event attributes
assert event.agent_key == "test_agent_key"
assert event.agent_role == "test_agent_role"
assert event.tool_name == "Test Tool"
assert event.tool_name == "test_tool"
assert event.tool_args == {"arg1": "value1"}
assert event.tool_class == "TestTool"
assert event.run_attempts == 1 # Default value from ToolUsage