Files
evotraders/backend/runtime/manager.py
cillin 59b44545d0 feat: Add agent workspace system and runtime management
- Add agent core modules (agent_core, factory, registry, skill_loader)
- Add runtime system for agent execution management
- Add REST API for agents, workspaces, and runtime control
- Add process supervisor for agent lifecycle management
- Add workspace template system with agent profiles
- Add frontend RuntimeView and runtime API integration
- Add per-agent skill workspaces for smoke_fullstack run
- Refactor skill system with active/installed separation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:43:29 +08:00

135 lines
4.7 KiB
Python

from __future__ import annotations
import json
from datetime import datetime, UTC
from pathlib import Path
from typing import Any, Dict, List, Optional
from .agent_runtime import AgentRuntimeState
from .context import TradingRunContext
from .registry import RuntimeRegistry
_global_runtime_manager: Optional["TradingRuntimeManager"] = None
def set_global_runtime_manager(manager: "TradingRuntimeManager") -> None:
global _global_runtime_manager
_global_runtime_manager = manager
def clear_global_runtime_manager() -> None:
global _global_runtime_manager
_global_runtime_manager = None
def get_global_runtime_manager() -> Optional["TradingRuntimeManager"]:
return _global_runtime_manager
class TradingRuntimeManager:
def __init__(self, config_name: str, run_dir: Path, bootstrap: Optional[Dict[str, Any]] = None) -> None:
self.config_name = config_name
self.run_dir = run_dir
self.bootstrap = bootstrap or {}
self.context: Optional[TradingRunContext] = None
self.registry = RuntimeRegistry()
self.current_session_key: Optional[str] = None
self.events: List[Dict[str, Any]] = []
self.pending_approvals: Dict[str, Dict[str, Any]] = {}
self.snapshot_path = self.run_dir / "state" / "runtime_state.json"
def prepare_run(self) -> TradingRunContext:
self.run_dir.mkdir(parents=True, exist_ok=True)
self.context = TradingRunContext(
config_name=self.config_name,
run_dir=self.run_dir,
bootstrap_values=self.bootstrap,
)
self._persist_snapshot()
return self.context
def set_session_key(self, session_key: str) -> None:
self.current_session_key = session_key
self._persist_snapshot()
def log_event(self, event: str, details: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
entry = {
"timestamp": datetime.now(UTC).isoformat(),
"event": event,
"details": details or {},
"session": self.current_session_key,
}
self.events.append(entry)
self._persist_snapshot()
return entry
def register_agent(self, agent_id: str) -> AgentRuntimeState:
state = AgentRuntimeState(agent_id=agent_id)
self.registry.register(agent_id, state)
self._persist_snapshot()
return state
def register_pending_approval(self, approval_id: str, payload: Dict[str, Any]) -> None:
payload.setdefault("status", "pending")
payload.setdefault("created_at", datetime.now(UTC).isoformat())
self.pending_approvals[approval_id] = payload
self._persist_snapshot()
def update_agent_status(
self,
agent_id: str,
status: str,
session_key: Optional[str] = None,
) -> AgentRuntimeState:
state = self.registry.get(agent_id)
if state is None:
state = self.register_agent(agent_id)
effective_session = session_key or self.current_session_key
state.update(status, effective_session)
self._persist_snapshot()
return state
def get_agent_state(self, agent_id: str) -> Optional[AgentRuntimeState]:
return self.registry.get(agent_id)
def list_agents(self) -> list[str]:
return self.registry.list_agents()
def resolve_pending_approval(self, approval_id: str, resolved_by: str, status: str) -> None:
entry = self.pending_approvals.get(approval_id)
if not entry:
return
entry["status"] = status
entry["resolved_at"] = datetime.now(UTC).isoformat()
entry["resolved_by"] = resolved_by
self._persist_snapshot()
def list_pending_approvals(self) -> List[Dict[str, Any]]:
return list(self.pending_approvals.values())
def build_snapshot(self) -> Dict[str, Any]:
return {
"context": {
"config_name": self.context.config_name,
"run_dir": str(self.context.run_dir),
"bootstrap_values": self.context.bootstrap_values,
}
if self.context
else None,
"current_session_key": self.current_session_key,
"agents": [
state.to_dict()
for agent_id in self.registry.list_agents()
if (state := self.registry.get(agent_id)) is not None
],
"events": self.events,
"pending_approvals": self.list_pending_approvals(),
}
def _persist_snapshot(self) -> None:
self.snapshot_path.parent.mkdir(parents=True, exist_ok=True)
self.snapshot_path.write_text(
json.dumps(self.build_snapshot(), ensure_ascii=False, indent=2),
encoding="utf-8",
)