Files
JARVIS/backend/app/tools/runtime/python_runtime.py

114 lines
3.0 KiB
Python
Raw Normal View History

"""
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()