Files

181 lines
5.7 KiB
Python

"""MetaAgent — Orchestrator agent for VoIdea.
Configurable from admin panel:
- cron_schedule: str (default "0 9 * * 1,4")
- weights: dict[agent_name, float]
- sources_count: int (default 10)
- auto_create: bool (default True)
Runs a subset of agents based on weights and collects reports.
"""
import json
from datetime import datetime, timezone
from typing import Any
from app.agents.base import AgentResult, AgentStatus, AgentTrigger, BaseAgent
def _get_registry():
from app.agents.registry import registry
return registry
DEFAULT_CONFIG: dict[str, Any] = {
"cron_schedule": "0 9 * * 1,4",
"weights": {},
"sources_count": 10,
"auto_create": True,
}
class MetaAgent(BaseAgent):
"""Orchestrator agent that coordinates other agents."""
name = "meta_agent"
version = "1.0.0"
description = "Orchestrates other agents based on configurable schedule and weights"
triggers = [
AgentTrigger.MANUAL,
AgentTrigger.CRON,
AgentTrigger.API,
]
def __init__(self, config: dict[str, Any] | None = None):
super().__init__()
self._config = {**DEFAULT_CONFIG, **(config or {})}
@property
def config(self) -> dict[str, Any]:
return self._config
@config.setter
def config(self, value: dict[str, Any]) -> None:
self._config = {**DEFAULT_CONFIG, **value}
async def run(self, context: dict[str, Any] | None = None) -> AgentResult:
"""Execute meta-agent orchestration.
Context can contain:
- action: str (orchestrate | report | status) — default "orchestrate"
- target_agents: list[str] — subset of agents to run (default: all)
- sources_count: int — override config
"""
await self.set_running("orchestration")
try:
action = (context or {}).get("action", "orchestrate")
if action == "status":
return await self._get_status()
elif action == "report":
return await self._generate_report()
else:
return await self._orchestrate(context or {})
except Exception as e:
await self.set_error(str(e))
return AgentResult(
success=False,
message=f"MetaAgent failed: {str(e)}",
errors=[str(e)],
)
async def health_check(self) -> bool:
return True
async def _orchestrate(self, context: dict[str, Any]) -> AgentResult:
"""Run target agents and collect results."""
target_agents: list[str] | None = context.get("target_agents")
sources_count = context.get("sources_count", self._config["sources_count"])
weights = self._config.get("weights", {})
all_agents = _get_registry().list_agents()
if not all_agents:
await self.set_idle()
return AgentResult(
success=True,
message="No agents registered",
data={"orchestrated": 0},
)
candidates = []
for a in all_agents:
name = a["name"]
if name == self.name:
continue
weight = weights.get(name, 1.0)
if weight <= 0:
continue
candidates.append((name, weight))
if not candidates:
candidates = [(a["name"], 1.0) for a in all_agents if a["name"] != self.name]
if target_agents:
candidates = [c for c in candidates if c[0] in target_agents]
max_sources = min(sources_count, len(candidates))
selected = candidates[:max_sources]
results: dict[str, Any] = {}
for name, _weight in selected:
try:
result = await _get_registry().run_agent(name, {"trigger": "meta_agent"})
results[name] = {
"success": result.success,
"message": result.message,
"duration_ms": result.duration_ms,
}
except Exception as e:
results[name] = {
"success": False,
"message": str(e),
}
success_count = sum(1 for r in results.values() if r["success"])
total_duration = sum(r.get("duration_ms", 0) for r in results.values())
await self.set_idle()
return AgentResult(
success=True,
message=f"Orchestrated {len(results)} agents ({success_count} ok)",
data={
"orchestrated": len(results),
"success_count": success_count,
"agents": results,
"total_duration_ms": total_duration,
"config": {
"cron_schedule": self._config["cron_schedule"],
"sources_count": sources_count,
"auto_create": self._config["auto_create"],
},
},
)
async def _generate_report(self) -> AgentResult:
"""Generate summary report of all agent statuses."""
all_agents = _get_registry().list_agents()
metrics = _get_registry().get_metrics_all()
return AgentResult(
success=True,
message=f"Report: {len(all_agents)} agents",
data={
"agents": all_agents,
"metrics": metrics,
"generated_at": datetime.now(timezone.utc).isoformat(),
},
)
async def _get_status(self) -> AgentResult:
return AgentResult(
success=True,
message="Meta-agent operational",
data={
"status": self.status.value,
"config": self._config,
"last_run": self._last_run.isoformat() if self._last_run else None,
},
)