81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
import asyncio
|
|
import json
|
|
import time
|
|
|
|
from tools import CalculatorArguments, ToolRegistry, ToolSpec
|
|
|
|
|
|
def test_builtin_tool_definitions() -> None:
|
|
registry = ToolRegistry()
|
|
|
|
definitions = registry.definitions()
|
|
|
|
assert [item["function"]["name"] for item in definitions] == [ # type: ignore[index]
|
|
"get_current_time",
|
|
"calculate",
|
|
]
|
|
|
|
|
|
def test_calculator_and_time_tools() -> None:
|
|
async def scenario() -> None:
|
|
registry = ToolRegistry()
|
|
calculation = await registry.execute(
|
|
"calculate", '{"expression":"(2 + 3) * 4"}'
|
|
)
|
|
current_time = await registry.execute(
|
|
"get_current_time", '{"timezone":"Asia/Shanghai"}'
|
|
)
|
|
|
|
assert calculation.is_error is False
|
|
assert json.loads(calculation.content)["result"] == 20
|
|
assert current_time.is_error is False
|
|
assert json.loads(current_time.content)["timezone"] == "Asia/Shanghai"
|
|
|
|
asyncio.run(scenario())
|
|
|
|
|
|
def test_tool_argument_and_expression_errors_are_safe() -> None:
|
|
async def scenario() -> None:
|
|
registry = ToolRegistry()
|
|
invalid_json = await registry.execute("calculate", "not-json")
|
|
unknown = await registry.execute("missing", "{}")
|
|
unsafe = await registry.execute(
|
|
"calculate", '{"expression":"__import__(\\"os\\").system(\\"id\\")"}'
|
|
)
|
|
invalid_timezone = await registry.execute(
|
|
"get_current_time", '{"timezone":"Not/A_Real_Zone"}'
|
|
)
|
|
|
|
assert invalid_json.is_error is True
|
|
assert "invalid_arguments" in invalid_json.content
|
|
assert unknown.is_error is True
|
|
assert "unknown_tool" in unknown.content
|
|
assert unsafe.is_error is True
|
|
assert "execution_failed" in unsafe.content
|
|
assert invalid_timezone.is_error is True
|
|
assert "execution_failed" in invalid_timezone.content
|
|
|
|
asyncio.run(scenario())
|
|
|
|
|
|
def test_tool_timeout_returns_error() -> None:
|
|
def slow_handler(arguments):
|
|
time.sleep(0.05)
|
|
return {"result": 1}
|
|
|
|
async def scenario() -> None:
|
|
registry = ToolRegistry(timeout_seconds=0.01)
|
|
registry._specs["calculate"] = ToolSpec(
|
|
name="calculate",
|
|
description="slow",
|
|
arguments_model=CalculatorArguments,
|
|
handler=slow_handler,
|
|
)
|
|
|
|
result = await registry.execute("calculate", '{"expression":"1+1"}')
|
|
|
|
assert result.is_error is True
|
|
assert "timeout" in result.content
|
|
|
|
asyncio.run(scenario())
|