huronvalley21 commited on
Commit
5940135
·
verified ·
1 Parent(s): 17dc482

Upload mythos/tool.py

Browse files
Files changed (1) hide show
  1. mythos/tool.py +102 -0
mythos/tool.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tool abstraction for Mythos agents — MCP-native design."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import inspect
7
+ from typing import Any, Awaitable, Callable, Optional
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class ToolSpec(BaseModel):
13
+ """Specification for a tool, compatible with MCP and function-calling APIs."""
14
+
15
+ name: str
16
+ description: str
17
+ parameters: dict[str, Any] = Field(default_factory=dict)
18
+ required: list[str] = Field(default_factory=list)
19
+ returns: Optional[str] = None
20
+
21
+
22
+ class Tool(BaseModel):
23
+ """A callable tool that agents can use."""
24
+
25
+ spec: ToolSpec
26
+ _func: Optional[Callable[..., Any]] = None
27
+
28
+ class Config:
29
+ arbitrary_types_allowed = True
30
+
31
+ def __init__(self, **data):
32
+ super().__init__(**data)
33
+ self._func = data.get("_func")
34
+
35
+ async def run(self, **kwargs) -> Any:
36
+ """Execute the tool with given arguments."""
37
+ if self._func is None:
38
+ raise RuntimeError(f"Tool '{self.spec.name}' has no implementation")
39
+ if asyncio.iscoroutinefunction(self._func):
40
+ return await self._func(**kwargs)
41
+ return self._func(**kwargs)
42
+
43
+ @classmethod
44
+ def from_function(
45
+ cls,
46
+ func: Callable[..., Any],
47
+ name: Optional[str] = None,
48
+ description: Optional[str] = None,
49
+ ) -> Tool:
50
+ """Create a Tool from a Python function."""
51
+ sig = inspect.signature(func)
52
+ params = {}
53
+ required = []
54
+ for param_name, param in sig.parameters.items():
55
+ param_type = param.annotation if param.annotation is not inspect.Parameter.empty else "string"
56
+ params[param_name] = {"type": str(param_type)}
57
+ if param.default is inspect.Parameter.empty:
58
+ required.append(param_name)
59
+
60
+ spec = ToolSpec(
61
+ name=name or func.__name__,
62
+ description=description or (func.__doc__ or ""),
63
+ parameters=params,
64
+ required=required,
65
+ )
66
+ return cls(spec=spec, _func=func)
67
+
68
+
69
+ class ToolRegistry(BaseModel):
70
+ """Registry of tools available to agents."""
71
+
72
+ tools: dict[str, Tool] = Field(default_factory=dict)
73
+
74
+ def register(self, tool: Tool) -> None:
75
+ """Register a tool."""
76
+ self.tools[tool.spec.name] = tool
77
+
78
+ def register_function(
79
+ self,
80
+ func: Callable[..., Any],
81
+ name: Optional[str] = None,
82
+ description: Optional[str] = None,
83
+ ) -> Tool:
84
+ """Register a function as a tool."""
85
+ tool = Tool.from_function(func, name, description)
86
+ self.register(tool)
87
+ return tool
88
+
89
+ def get(self, name: str) -> Optional[Tool]:
90
+ """Get a tool by name."""
91
+ return self.tools.get(name)
92
+
93
+ def list_tools(self) -> list[ToolSpec]:
94
+ """List all tool specifications."""
95
+ return [t.spec for t in self.tools.values()]
96
+
97
+ async def call(self, name: str, **kwargs) -> Any:
98
+ """Call a tool by name."""
99
+ tool = self.get(name)
100
+ if tool is None:
101
+ raise KeyError(f"Tool '{name}' not found")
102
+ return await tool.run(**kwargs)