feat(tools): Phase T.1-T.4 complete - manifest system, registry, implementations, runtime, collaboration, scheduler

This commit is contained in:
2026-04-05 11:54:57 +08:00
parent fca7a7cf3d
commit 10d9340c53
30 changed files with 2891 additions and 4 deletions

View File

@@ -0,0 +1,113 @@
"""
Python Runtime
Native Python tool execution runtime.
"""
import asyncio
import importlib.util
import sys
from pathlib import Path
from typing import Any, Callable, Dict
from tools.runtime.base import BaseRuntime
class PythonRuntime(BaseRuntime):
"""Python runtime for executing Python-based tools"""
def __init__(self):
self._executors: Dict[str, Callable] = {}
self._modules: Dict[str, Any] = {}
def get_name(self) -> str:
return "python"
async def validate(self, entry: str) -> bool:
"""Validate Python tool entry point"""
path = Path(entry)
if not path.exists():
return False
if path.suffix != ".py":
return False
return True
async def execute(
self,
entry: str,
command: str,
parameters: Dict[str, Any],
timeout: int,
) -> Dict[str, Any]:
"""Execute a Python tool"""
try:
# Load module dynamically
module = self._load_module(entry, command)
if module is None:
return {
"status": "error",
"error": f"Failed to load module from {entry}",
}
# Get the execute function
if not hasattr(module, "execute"):
return {
"status": "error",
"error": "Module does not have 'execute' function",
}
execute_func = module.execute
# Run in executor to avoid blocking
loop = asyncio.get_event_loop()
result = await asyncio.wait_for(
loop.run_in_executor(
None,
lambda: execute_func(command, parameters),
),
timeout=timeout / 1000,
)
return {
"status": "success",
"result": result,
}
except asyncio.TimeoutError:
return {
"status": "error",
"error": f"Execution timed out after {timeout}ms",
}
except Exception as e:
return {
"status": "error",
"error": str(e),
}
def _load_module(self, entry: str, command: str) -> Any:
"""Load Python module from file path"""
cache_key = f"{entry}:{command}"
if cache_key in self._modules:
return self._modules[cache_key]
try:
path = Path(entry)
module_name = path.stem
spec = importlib.util.spec_from_file_location(module_name, entry)
if spec is None or spec.loader is None:
return None
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
self._modules[cache_key] = module
return module
except Exception:
return None
def clear_cache(self) -> None:
"""Clear module cache"""
self._modules.clear()