registry.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. from __future__ import annotations
  2. from collections.abc import Callable
  3. from typing import Any
  4. from supply_agent.tools.base import Tool
  5. from supply_agent.types import ToolDefinition, ToolResult
  6. class ToolRegistry:
  7. """Registry for managing and executing agent tools."""
  8. def __init__(self) -> None:
  9. self._tools: dict[str, Tool] = {}
  10. def register(
  11. self,
  12. func: Callable[..., Any],
  13. name: str | None = None,
  14. description: str | None = None,
  15. ) -> Tool:
  16. """Register a function as a tool."""
  17. tool = Tool(func, name=name, description=description)
  18. self._tools[tool.name] = tool
  19. return tool
  20. def register_tool(self, tool: Tool) -> None:
  21. """Register an existing Tool instance."""
  22. self._tools[tool.name] = tool
  23. def unregister(self, name: str) -> None:
  24. self._tools.pop(name, None)
  25. def get(self, name: str) -> Tool | None:
  26. return self._tools.get(name)
  27. def list_tools(self) -> list[str]:
  28. return list(self._tools.keys())
  29. @property
  30. def definitions(self) -> list[ToolDefinition]:
  31. return [t.definition for t in self._tools.values()]
  32. def execute(self, tool_call_id: str, name: str, arguments: str) -> ToolResult:
  33. """Execute a tool by name and return the result."""
  34. tool = self._tools.get(name)
  35. if not tool:
  36. return ToolResult(
  37. tool_call_id=tool_call_id,
  38. name=name,
  39. content=f'{{"error": "Tool \'{name}\' not found"}}',
  40. is_error=True,
  41. )
  42. content = tool.execute(arguments)
  43. is_error = content.startswith('{"error"')
  44. return ToolResult(
  45. tool_call_id=tool_call_id,
  46. name=name,
  47. content=content,
  48. is_error=is_error,
  49. )
  50. async def aexecute(self, tool_call_id: str, name: str, arguments: str) -> ToolResult:
  51. """Async execute a tool."""
  52. tool = self._tools.get(name)
  53. if not tool:
  54. return ToolResult(
  55. tool_call_id=tool_call_id,
  56. name=name,
  57. content=f'{{"error": "Tool \'{name}\' not found"}}',
  58. is_error=True,
  59. )
  60. content = await tool.aexecute(arguments)
  61. is_error = content.startswith('{"error"')
  62. return ToolResult(
  63. tool_call_id=tool_call_id,
  64. name=name,
  65. content=content,
  66. is_error=is_error,
  67. )
  68. def from_decorated(self, *funcs: Callable[..., Any]) -> ToolRegistry:
  69. """Register multiple @tool-decorated functions."""
  70. for func in funcs:
  71. if getattr(func, "_is_tool", False):
  72. self.register(func)
  73. return self
  74. def __len__(self) -> int:
  75. return len(self._tools)
  76. def __contains__(self, name: str) -> bool:
  77. return name in self._tools