Files
JARVIS/backend/app/tools/discovery.py

130 lines
3.8 KiB
Python

"""
Tool Discovery
Automatic tool discovery from manifest files with hot reload support.
"""
from pathlib import Path
from typing import List, Dict, Any, Optional, Callable
import asyncio
class ToolDiscovery:
"""Tool automatic discovery"""
def __init__(self, manifest_dir: Optional[Path] = None):
if manifest_dir is None:
manifest_dir = Path(__file__).parent / "manifests"
self.manifest_dir = Path(manifest_dir)
def discover(self) -> List[Path]:
"""Discover all manifest files"""
manifests = list(self.manifest_dir.glob("**/*.yaml"))
manifests.extend(self.manifest_dir.glob("**/*.yml"))
manifests.extend(self.manifest_dir.glob("**/*.json"))
return manifests
def discover_by_tag(self, tag: str) -> List[Path]:
"""Discover manifests by tag"""
import yaml
results = []
for manifest_path in self.discover():
try:
with open(manifest_path, encoding="utf-8") as f:
data = yaml.safe_load(f)
if data and tag in data.get("tags", []):
results.append(manifest_path)
except Exception:
continue
return results
async def hot_reload(self, registry: Any) -> Dict[str, bool]:
"""Hot reload all tools in registry"""
results = {}
for manifest_path in self.discover():
tool_name = manifest_path.stem
try:
executor = load_executor(manifest_path)
await registry.register(str(manifest_path), executor)
results[tool_name] = True
except Exception as e:
results[tool_name] = False
return results
def load_executor(manifest_path: Path) -> Callable:
"""Load tool executor from manifest"""
import yaml
with open(manifest_path, encoding="utf-8") as f:
manifest = yaml.safe_load(f)
runtime = manifest.get("runtime", "python")
if runtime == "python":
return load_python_executor(manifest)
elif runtime == "javascript":
return load_js_executor(manifest)
else:
return load_native_executor(manifest)
def load_python_executor(manifest: dict) -> Callable:
"""Load Python executor"""
entry = manifest.get("entry", "")
tool_name = manifest.get("name", "")
def executor(command: str, parameters: dict) -> dict:
return {
"status": "success",
"result": f"Python tool {tool_name} executed {command}",
"message": f"Tool {tool_name} is not yet fully implemented",
}
return executor
def load_js_executor(manifest: dict) -> Callable:
"""Load JavaScript executor"""
tool_name = manifest.get("name", "")
def executor(command: str, parameters: dict) -> dict:
return {
"status": "success",
"result": f"JS tool {tool_name} executed {command}",
"message": f"Tool {tool_name} requires Node.js runtime",
}
return executor
def load_native_executor(manifest: dict) -> Callable:
"""Load native executor"""
tool_name = manifest.get("name", "")
def executor(command: str, parameters: dict) -> dict:
return {
"status": "success",
"result": f"Native tool {tool_name} executed {command}",
"message": f"Tool {tool_name} requires native binary",
}
return executor
async def load_all_tools(registry: Any, manifest_dir: Optional[Path] = None) -> int:
"""Load all tools from manifest directory"""
discovery = ToolDiscovery(manifest_dir)
count = 0
for manifest_path in discovery.discover():
try:
executor = load_executor(manifest_path)
await registry.register(str(manifest_path), executor)
count += 1
except Exception:
continue
return count