262 lines
25 KiB
Python
262 lines
25 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import timedelta
|
|
from statistics import median
|
|
|
|
from django.utils import timezone
|
|
|
|
from agents.progeny import ProgenyService
|
|
from control_plane.agents.models import Agent, AgentCompetency, AgentPlan, AgentRole, AgentRun, AgentScope, AgentTeam, AgentTeamMember, AgentVersion, BenchmarkRun, Competency, ImprovementCandidate, ProgenySignal, PromotionStatus, ProgenyInvestigation, ProgenyExperiment, ReplayRun
|
|
from control_plane.events.bus import EventBus
|
|
from control_plane.projects.models import Artifact, CommitRecord, Project, Task, TaskAttempt
|
|
from control_plane.resources.models import ModelRequest, Resource
|
|
from control_plane.verification.models import Review, TestRun, Verification, VerificationLevel, VerificationResult
|
|
from graph.models import GraphRun
|
|
from model_router.router import ModelCapability, ModelRequestContract, ModelRouter
|
|
|
|
|
|
COMPETENCIES = [
|
|
("django_backend", "Django Backend", "backend"),
|
|
("frontend_design", "Frontend/Product Design", "frontend"),
|
|
("frontend_engineering", "Frontend Engineering", "frontend"),
|
|
("testing", "Testing", "quality"),
|
|
("security_review", "Security Review", "quality"),
|
|
("architecture", "Architecture", "strategy"),
|
|
("product_strategy", "Product Strategy", "strategy"),
|
|
("repository_archaeology", "Repository Archaeology", "analysis"),
|
|
("performance_analysis", "Performance Analysis", "quality"),
|
|
("accessibility", "Accessibility", "frontend"),
|
|
("model_training", "Model Training", "ml"),
|
|
]
|
|
|
|
|
|
class AgentControlRoomService:
|
|
def __init__(self, router: ModelRouter | None = None, bus: EventBus | None = None) -> None:
|
|
self.router = router
|
|
self.bus = bus or EventBus()
|
|
|
|
def ensure_competencies(self) -> list[Competency]:
|
|
competencies = []
|
|
for key, name, domain in COMPETENCIES:
|
|
competency, _ = Competency.objects.get_or_create(key=key, defaults={"name": name, "domain": domain, "description": f"{name} competency."})
|
|
competencies.append(competency)
|
|
return competencies
|
|
|
|
def assign_competency(self, version: AgentVersion, key: str, *, proficiency: float = 0.5, confidence: float = 0.5, evidence: dict[str, object] | None = None, source: str = "control_room") -> AgentCompetency:
|
|
competency, _ = Competency.objects.get_or_create(key=key, defaults={"name": key.replace("_", " ").title()})
|
|
assignment, _ = AgentCompetency.objects.update_or_create(agent_version=version, competency=competency, defaults={"agent": version.agent, "proficiency": proficiency, "confidence": confidence, "evidence": evidence or {}, "source": source, "last_evaluated_at": timezone.now()})
|
|
return assignment
|
|
|
|
def bootstrap_frontend_agents(self) -> dict[str, AgentVersion]:
|
|
self.ensure_competencies()
|
|
specs = [
|
|
("Product / Frontend Design Agent", AgentRole.FRONTEND_DESIGNER, "sol", ["frontend_design", "product_strategy", "architecture", "accessibility"], "Produce stack-neutral InformationArchitecture, PageSpec, ComponentSpec, InteractionSpec, DesignTokens, ResponsiveRules, AccessibilityRequirements, and FrontendImplementationPlan. Do not assume React; preserve Django templates/HTMX/Alpine where appropriate."),
|
|
("Frontend Engineer", AgentRole.FRONTEND_ENGINEER, "qwen", ["frontend_engineering", "django_backend", "testing", "accessibility"], "Implement frontend work after inspecting the project stack. Preserve existing architecture; prefer Django templates/HTMX/Alpine for Django projects unless a plan explicitly chooses another framework."),
|
|
("UX / Accessibility Reviewer", AgentRole.UX_ACCESSIBILITY_REVIEWER, "qwen", ["accessibility", "frontend_design", "testing"], "Review UX, accessibility, responsive states, keyboard behavior, and implementation fit without forcing a framework."),
|
|
("Visual Judge", AgentRole.VISUAL_JUDGE, "qwen", ["frontend_design", "accessibility"], "Placeholder visual evaluator until screenshot infrastructure is available; judge design contracts and static artifacts."),
|
|
]
|
|
versions: dict[str, AgentVersion] = {}
|
|
for name, role, model, competencies, contract in specs:
|
|
agent, created = Agent.objects.get_or_create(name=name, defaults={"role": role, "description": contract, "purpose": contract, "scope": AgentScope.GLOBAL})
|
|
version, version_created = AgentVersion.objects.get_or_create(agent=agent, version=1, defaults={"model": model, "system_contract": contract, "capabilities": competencies, "context_policy": {"include_raw_secrets": False, "inspect_project_stack": True}, "retrieval_policy": {"project_stack_first": True}, "tool_policy": {"framework_neutral": True}, "retry_policy": {"max_retries": 1}, "permissions": {"design_only": role == AgentRole.FRONTEND_DESIGNER}, "promotion_status": PromotionStatus.CHALLENGER, "benchmark_status": "SEEDED", "creation_source": "agent_control_room", "scope": AgentScope.GLOBAL})
|
|
if version_created:
|
|
self.bus.publish("AGENT_VERSION_CREATED", actor="agent_control_room", payload={"agent": name, "version": version.version})
|
|
if created:
|
|
self.bus.publish("AGENT_CREATED", actor="agent_control_room", payload={"agent": name})
|
|
if agent.champion_version_id is None:
|
|
agent.champion_version = version
|
|
version.promotion_status = PromotionStatus.CHAMPION
|
|
version.immutable_since = timezone.now()
|
|
version.save(update_fields=["promotion_status", "immutable_since", "updated_at"])
|
|
agent.save(update_fields=["champion_version", "updated_at"])
|
|
for competency in competencies:
|
|
self.assign_competency(version, competency, proficiency=0.7, confidence=0.7, evidence={"bootstrap": True})
|
|
versions[name] = version
|
|
return versions
|
|
|
|
def frontend_agent_audit(self) -> dict[str, object]:
|
|
coverage = {
|
|
"Product / UX Design": AgentVersion.objects.filter(agent__role=AgentRole.FRONTEND_DESIGNER).exists(),
|
|
"Frontend Architecture": AgentCompetency.objects.filter(competency__key="architecture", agent_version__agent__role=AgentRole.FRONTEND_DESIGNER).exists(),
|
|
"Frontend Engineering": AgentVersion.objects.filter(agent__role=AgentRole.FRONTEND_ENGINEER).exists(),
|
|
"Accessibility Review": AgentVersion.objects.filter(agent__role=AgentRole.UX_ACCESSIBILITY_REVIEWER).exists(),
|
|
"Visual / UX Review": AgentVersion.objects.filter(agent__role__in=[AgentRole.UX_ACCESSIBILITY_REVIEWER, AgentRole.VISUAL_JUDGE]).exists(),
|
|
}
|
|
return {"coverage": coverage, "missing": [name for name, present in coverage.items() if not present]}
|
|
|
|
def create_software_feature_team(self) -> AgentTeam:
|
|
versions = self.bootstrap_frontend_agents()
|
|
coder = AgentVersion.objects.filter(agent__role=AgentRole.CODER, promotion_status=PromotionStatus.CHAMPION).first()
|
|
reviewer = AgentVersion.objects.filter(agent__role=AgentRole.REVIEWER, promotion_status=PromotionStatus.CHAMPION).first()
|
|
team, created = AgentTeam.objects.get_or_create(name="Software Feature Team", defaults={"purpose": "Design, implement, and review software features.", "scope": AgentScope.GLOBAL})
|
|
if created:
|
|
self.bus.publish("TEAM_CREATED", payload={"team_id": str(team.id), "name": team.name})
|
|
for role, version in [("product_frontend_design", versions["Product / Frontend Design Agent"]), ("frontend_engineering", versions["Frontend Engineer"]), ("backend_coding", coder), ("review_quality", reviewer)]:
|
|
if version is None:
|
|
continue
|
|
_, member_created = AgentTeamMember.objects.get_or_create(team=team, agent_version=version, role=role, defaults={"responsibilities": [role]})
|
|
if member_created:
|
|
self.bus.publish("TEAM_MEMBER_ADDED", payload={"team_id": str(team.id), "agent_version_id": str(version.id), "role": role})
|
|
return team
|
|
|
|
def list_agents(self) -> list[dict[str, object]]:
|
|
return [self._agent(agent) for agent in Agent.objects.select_related("champion_version").order_by("name")]
|
|
|
|
def get_agent(self, agent_id) -> dict[str, object]:
|
|
return self._agent(Agent.objects.get(id=agent_id), include_versions=True)
|
|
|
|
def get_agent_version(self, version_id) -> dict[str, object]:
|
|
version = AgentVersion.objects.select_related("agent", "parent_version").get(id=version_id)
|
|
return {"id": str(version.id), "agent_id": str(version.agent_id), "agent": version.agent.name, "version": version.version, "model": version.model, "model_config": version.model_config, "system_contract": version.system_contract, "context_policy": version.context_policy, "retrieval_policy": version.retrieval_policy, "tool_policy": version.tool_policy, "retry_policy": version.retry_policy, "workflow": version.workflow, "permissions": version.permissions, "resource_preferences": version.resource_preferences, "scope": version.scope, "parent_version_id": str(version.parent_version_id) if version.parent_version_id else None, "ancestry": version.ancestry, "creation_source": version.creation_source, "benchmark_status": version.benchmark_status, "promotion_status": version.promotion_status, "competencies": list(version.competencies.select_related("competency").values("competency__key", "competency__name", "proficiency", "confidence", "evidence", "source"))}
|
|
|
|
def get_agent_usage(self, version_id) -> dict[str, object]:
|
|
version = AgentVersion.objects.get(id=version_id)
|
|
role = version.agent.role
|
|
attempts = TaskAttempt.objects.filter(coder=version)
|
|
commits = CommitRecord.objects.filter(coder=version) | CommitRecord.objects.filter(reviewer=version) | CommitRecord.objects.filter(judge=version)
|
|
return {"execution_graph_versions": list(version.experiment_variants.exclude(execution_graph_version=None).values("execution_graph_version__graph__name", "execution_graph_version__version").distinct()), "graph_runs": GraphRun.objects.filter(task__attempts__coder=version).distinct().count(), "projects": list(attempts.exclude(task__project=None).values_list("task__project__name", flat=True).distinct()), "task_types": list(attempts.values_list("task__task_type", flat=True).distinct()), "recent_agent_runs": list(version.runs.order_by("-created_at").values("id", "status", "metrics")[:10]), "replay_experiments": ProgenyExperiment.objects.filter(variants__agent_version=version).distinct().count(), "commit_count": commits.distinct().count(), "logical_role": role}
|
|
|
|
def get_agent_performance(self, version_id, *, window: str = "lifetime", project_id=None, task_type: str | None = None, last_n: int | None = None) -> dict[str, object]:
|
|
version = AgentVersion.objects.get(id=version_id)
|
|
attempts = TaskAttempt.objects.filter(coder=version).select_related("task")
|
|
if project_id:
|
|
attempts = attempts.filter(task__project_id=project_id)
|
|
if task_type:
|
|
attempts = attempts.filter(task__task_type=task_type)
|
|
if window == "7d":
|
|
attempts = attempts.filter(created_at__gte=timezone.now() - timedelta(days=7))
|
|
if window == "30d":
|
|
attempts = attempts.filter(created_at__gte=timezone.now() - timedelta(days=30))
|
|
if last_n:
|
|
ids = list(attempts.order_by("-created_at").values_list("id", flat=True)[:last_n])
|
|
attempts = TaskAttempt.objects.filter(id__in=ids)
|
|
tasks = Task.objects.filter(attempts__in=attempts).distinct()
|
|
task_count = tasks.count()
|
|
complete = tasks.filter(status="COMPLETE").count()
|
|
test_runs = TestRun.objects.filter(task__in=tasks)
|
|
reviews = Review.objects.filter(task__in=tasks)
|
|
judges = Verification.objects.filter(task__in=tasks, level=VerificationLevel.TASK)
|
|
requests = ModelRequest.objects.filter(agent_version=version)
|
|
latencies = [item for item in requests.exclude(latency_ms=None).values_list("latency_ms", flat=True)]
|
|
telemetry = [run.metadata.get("telemetry", {}) for run in GraphRun.objects.filter(task__in=tasks)]
|
|
mutation_failures = sum(int(t.get("patch_mismatches", 0) or 0) + int(t.get("write_file_fallbacks", 0) or 0) for t in telemetry)
|
|
return {"window": window, "filters": {"project_id": str(project_id) if project_id else None, "task_type": task_type, "last_n": last_n}, "quality": {"task_count": task_count, "task_completion_rate": complete / task_count if task_count else None, "reviewer_pass": reviews.filter(status="PASS").count(), "reviewer_rework": reviews.filter(status="REWORK_REQUIRED").count(), "reviewer_reject": reviews.filter(status="REJECTED").count(), "judge_pass_rate": judges.filter(result=VerificationResult.PASS).count() / judges.count() if judges.count() else None, "test_pass_rate": test_runs.filter(status="PASS").count() / test_runs.count() if test_runs.count() else None}, "robustness": {"retry_rate": tasks.exclude(retry_count=0).count() / task_count if task_count else None, "retry_exhaustion": tasks.filter(status="FAILED").count(), "malformed_model_output": ProgenySignal.objects.filter(agent_version=version, failure_category__icontains="MALFORMED").count(), "mutation_failures": mutation_failures, "provider_failures": requests.exclude(failure_reason="").count()}, "efficiency": {"median_runtime_ms": median(latencies) if latencies else None, "model_requests_per_task": requests.count() / task_count if task_count else None, "tokens_per_task": (sum(r.prompt_tokens or 0 for r in requests) + sum(r.completion_tokens or 0 for r in requests)) / task_count if task_count else None, "mutation_operations_per_task": sum(int(t.get("mutation_operations", 0) or 0) for t in telemetry) / task_count if task_count else None}, "systemic": {"progeny_signals": ProgenySignal.objects.filter(agent_version=version).count(), "investigations": ProgenyInvestigation.objects.filter(signals__agent_version=version).distinct().count(), "scenario_failures": version.agent.project.scenario_findings.count() if version.agent.project_id else 0}}
|
|
|
|
def get_agent_health(self, version_id) -> dict[str, object]:
|
|
version = AgentVersion.objects.get(id=version_id)
|
|
perf = self.get_agent_performance(version_id, window="30d")
|
|
reasons = []
|
|
status = "HEALTHY"
|
|
completion = perf["quality"]["task_completion_rate"]
|
|
test_pass = perf["quality"]["test_pass_rate"]
|
|
unresolved = ProgenySignal.objects.filter(agent_version=version, status="OPEN").count()
|
|
provider_available = self._provider_health(version)
|
|
if version.promotion_status == PromotionStatus.CHALLENGER:
|
|
status = "CHALLENGED"
|
|
reasons.append("Version is a challenger awaiting benchmark/promotion evidence.")
|
|
if version.promotion_status == PromotionStatus.DISABLED or version.status == "DISABLED":
|
|
status = "DISABLED"
|
|
reasons.append("Version is disabled.")
|
|
if completion is not None and completion < 0.5:
|
|
status = "DEGRADED"
|
|
reasons.append("Recent task completion rate below 50%.")
|
|
if test_pass is not None and test_pass < 0.7 and status != "DEGRADED":
|
|
status = "WATCH"
|
|
reasons.append("Recent test pass rate below 70%.")
|
|
if unresolved:
|
|
status = "DEGRADED" if unresolved >= 3 else ("WATCH" if status == "HEALTHY" else status)
|
|
reasons.append(f"{unresolved} unresolved Progeny signal(s).")
|
|
if provider_available == "UNAVAILABLE":
|
|
status = "DEGRADED"
|
|
reasons.append("Configured provider is unavailable.")
|
|
return {"status": status, "reasons": reasons or ["No negative health evidence in selected window."], "evidence": {"performance": perf, "provider_health": provider_available, "benchmark_status": version.benchmark_status}}
|
|
|
|
def get_agent_progeny(self, version_id) -> dict[str, object]:
|
|
version = AgentVersion.objects.get(id=version_id)
|
|
return {"signals": list(ProgenySignal.objects.filter(agent_version=version).values("id", "source", "severity", "failure_category", "summary", "status")), "investigations": list(ProgenyInvestigation.objects.filter(signals__agent_version=version).distinct().values("id", "status", "recommended_target", "recommended_route", "confidence")), "improvement_candidates": list(version.improvement_candidates.values("id", "status", "hypothesis", "recommended_route"))}
|
|
|
|
def get_agent_challengers(self, agent_id) -> dict[str, object]:
|
|
agent = Agent.objects.get(id=agent_id)
|
|
return {"champion": str(agent.champion_version_id) if agent.champion_version_id else None, "challengers": [self.get_agent_version(v.id) for v in agent.versions.filter(promotion_status=PromotionStatus.CHALLENGER)], "benchmarks": list(BenchmarkRun.objects.filter(champion__agent=agent).values("id", "champion_id", "challenger_id", "metrics", "decision"))}
|
|
|
|
def investigate_agent(self, version: AgentVersion) -> ProgenyInvestigation:
|
|
signal = ProgenySignal.objects.create(agent_version=version, source="agent_control_room", severity="MEDIUM", failure_category="AGENT_HEALTH", summary=f"Investigate {version.agent.name} v{version.version} based on Control Room telemetry.", evidence={"performance": self.get_agent_performance(version.id), "health": self.get_agent_health(version.id)}, grouping_key=f"agent:{version.id}:health")
|
|
self.bus.publish("AGENT_INVESTIGATION_STARTED", actor="agent_control_room", payload={"agent_version_id": str(version.id), "signal_id": str(signal.id)})
|
|
investigation = ProgenyService(self.bus).create_smart_investigation(signal.grouping_key)
|
|
self.bus.publish("AGENT_INVESTIGATION_COMPLETED", actor="agent_control_room", payload={"agent_version_id": str(version.id), "investigation_id": str(investigation.id)})
|
|
return investigation
|
|
|
|
def create_improvement_candidate(self, investigation: ProgenyInvestigation) -> ImprovementCandidate:
|
|
return ProgenyService(self.bus).create_improvement_candidate(investigation)
|
|
|
|
def extend_agent(self, version: AgentVersion, competency_key: str) -> AgentPlan:
|
|
competency, _ = Competency.objects.get_or_create(key=competency_key, defaults={"name": competency_key.replace("_", " ").title()})
|
|
return AgentPlan.objects.create(agent=version.agent, name=f"Extend {version.agent.name} with {competency.name}", role=version.agent.role, model=version.model, system_contract=version.system_contract, capabilities=[*version.capabilities, competency_key], tools=version.tools, permissions=version.permissions, context_policy=version.context_policy, workflow=version.workflow, benchmarks=[{"competency": competency_key}], success_criteria={"competency_added": competency_key}, target_agent_version=version, plan_type="EXTEND", scope=version.scope, evidence={"parent_version_id": str(version.id)}, status="DRAFT", created_by="agent_control_room")
|
|
|
|
def evolve_agent(self, version: AgentVersion, objective: str, baseline: dict[str, object]) -> ImprovementCandidate:
|
|
investigation = ProgenyInvestigation.objects.create(signal_clusters=[], affected_agents=[str(version.id)], affected_projects=[], affected_graph_versions=[], affected_nodes=[], hypotheses=[{"hypothesis": objective, "baseline": baseline}], recommended_target="AGENT", confidence=0.7, recommended_route="ReplayArena", proposed_experiments=["Replay recent failures against challenger prompt/tool policy."], expected_impact=objective, estimated_cost="MEDIUM")
|
|
return ImprovementCandidate.objects.create(investigation=investigation, target_type="AGENT", target_id=str(version.id), target_label=f"{version.agent.name} v{version.version}", hypothesis=objective, recommended_route="ReplayArena", evidence={"baseline": baseline}, agent_version=version)
|
|
|
|
def fork_agent(self, version: AgentVersion, *, name: str, scope: str = AgentScope.GLOBAL, project: Project | None = None) -> AgentVersion:
|
|
agent = Agent.objects.create(name=name, role=version.agent.role, description=f"Specialized descendant of {version.agent.name}", purpose=version.agent.purpose, scope=scope, project=project)
|
|
child = AgentVersion.objects.create(agent=agent, version=1, model=version.model, model_config=version.model_config, system_contract=version.system_contract, capabilities=version.capabilities, tools=version.tools, permissions=version.permissions, context_policy=version.context_policy, retrieval_policy=version.retrieval_policy, tool_policy=version.tool_policy, workflow=version.workflow, retry_policy=version.retry_policy, resource_preferences=version.resource_preferences, graph_usage_policy=version.graph_usage_policy, scope=scope, parent_version=version, ancestry=[*version.ancestry, str(version.id)], creation_source="fork", promotion_status=PromotionStatus.CHALLENGER, benchmark_status="REQUIRED")
|
|
self.bus.publish("AGENT_CHALLENGER_CREATED", actor="agent_control_room", payload={"parent_version_id": str(version.id), "child_version_id": str(child.id)})
|
|
return child
|
|
|
|
def create_frontend_design_artifact(self, project: Project, version: AgentVersion) -> Artifact:
|
|
contract = {"InformationArchitecture": ["Project list", "Project workspace", "Agent Control Room", "Roadmap", "Scenario Lab"], "PageSpec": {"ProjectWorkspace": "Stack-neutral workspace for project status, lifecycle actions, evidence, and approvals."}, "ComponentSpec": ["LifecycleStatusPanel", "EvidenceTimeline", "ApprovalQueue", "AgentHealthCard"], "InteractionSpec": ["Filter lifecycle items", "Open evidence detail", "Approve gated action"], "DesignTokens": {"semantic": ["surface", "accent", "danger", "warning", "success"]}, "ResponsiveRules": ["Single-column mobile", "Two-column tablet", "Dashboard grid desktop"], "AccessibilityRequirements": ["Keyboard reachable actions", "Visible focus", "WCAG AA contrast", "ARIA labels for status badges"], "FrontendImplementationPlan": {"framework_neutral": True, "django_compatible": True, "preferred_for_django": ["Django templates", "HTMX", "Alpine", "project CSS/Tailwind if present"], "react_required": False}}
|
|
prompt = "Produce a stack-neutral UI specification for the future Artifex Project Workspace. Do not force React; support Django/HTMX."
|
|
if self.router:
|
|
try:
|
|
response = self.router.complete(ModelRequestContract(purpose=ModelCapability.PLANNING, model_hint=version.model, agent_version=version, project=project, prompt=prompt))
|
|
parsed = json.loads(response.content)
|
|
if isinstance(parsed, dict):
|
|
contract = parsed
|
|
except Exception:
|
|
pass
|
|
return Artifact.objects.create(project=project, artifact_type="FRONTEND_DESIGN_SPEC", name="Artifex Project Workspace UI Spec", content=contract, generated_by=f"{version.agent.name} v{version.version}")
|
|
|
|
def list_teams(self) -> list[dict[str, object]]:
|
|
return [self._team(team) for team in AgentTeam.objects.order_by("name")]
|
|
|
|
def get_team(self, team_id) -> dict[str, object]:
|
|
return self._team(AgentTeam.objects.get(id=team_id))
|
|
|
|
def _agent(self, agent: Agent, *, include_versions: bool = False) -> dict[str, object]:
|
|
data = {"id": str(agent.id), "name": agent.name, "role": agent.role, "purpose": agent.purpose, "scope": agent.scope, "status": agent.status, "champion_version": str(agent.champion_version_id) if agent.champion_version_id else None, "competencies": list(agent.competencies.select_related("competency").values("competency__key", "proficiency", "confidence"))}
|
|
if include_versions:
|
|
data["versions"] = [self.get_agent_version(version.id) for version in agent.versions.order_by("version")]
|
|
return data
|
|
|
|
def _team(self, team: AgentTeam) -> dict[str, object]:
|
|
return {"id": str(team.id), "name": team.name, "purpose": team.purpose, "scope": team.scope, "status": team.status, "members": list(team.members.select_related("agent_version__agent").values("role", "responsibilities", "agent_version_id", "agent_version__agent__name", "agent_version__version", "status")), "metadata": team.metadata}
|
|
|
|
def _provider_health(self, version: AgentVersion) -> str:
|
|
resource = None
|
|
for candidate in Resource.objects.filter(is_active=True):
|
|
model = str(candidate.config.get("model", "")).lower()
|
|
provider = candidate.provider.lower()
|
|
if version.model in candidate.roles or version.model.lower() in provider or version.model.lower() in model:
|
|
resource = candidate
|
|
break
|
|
if version.model == "qwen" and provider == "local_inference":
|
|
resource = candidate
|
|
break
|
|
if version.model == "sol" and provider == "opencode":
|
|
resource = candidate
|
|
break
|
|
if resource is None:
|
|
return "UNKNOWN"
|
|
if resource.health_status != "UNKNOWN":
|
|
return resource.health_status
|
|
try:
|
|
from model_router.providers import QwenProvider, SolProvider
|
|
|
|
if resource.provider == "local_inference":
|
|
return QwenProvider(resource).health()
|
|
if resource.provider == "opencode":
|
|
return SolProvider(resource).health()
|
|
except Exception:
|
|
return "UNKNOWN"
|
|
return resource.health_status
|