"""Aggregated skill learning models.""" from __future__ import annotations from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Any LEARNING_CANDIDATE_STATUSES = { "open", "queued", "synthesizing", "draft_ready", "safety_failed", "eval_failed", "review_pending", "approved", "rejected", "published", "failed", "superseded", } RISK_LEVELS = {"low", "medium", "high", "critical"} @dataclass(slots=True) class SkillPerformanceSnapshot: skill_name: str skill_version: str activation_count: int success_count: int failure_count: int latest_used_at: str last_feedback_score: float | None = None def to_dict(self) -> dict[str, Any]: return { "skill_name": self.skill_name, "skill_version": self.skill_version, "activation_count": self.activation_count, "success_count": self.success_count, "failure_count": self.failure_count, "latest_used_at": self.latest_used_at, "last_feedback_score": self.last_feedback_score, } @classmethod def from_dict(cls, payload: dict[str, Any]) -> "SkillPerformanceSnapshot": value = payload.get("last_feedback_score") return cls( skill_name=str(payload["skill_name"]), skill_version=str(payload["skill_version"]), activation_count=int(payload.get("activation_count", 0) or 0), success_count=int(payload.get("success_count", 0) or 0), failure_count=int(payload.get("failure_count", 0) or 0), latest_used_at=str(payload.get("latest_used_at") or ""), last_feedback_score=None if value in (None, "") else float(value), ) @dataclass(slots=True) class SkillLearningCandidate: candidate_id: str kind: str source_run_ids: list[str] source_session_ids: list[str] related_skill_names: list[str] reason: str evidence: dict[str, Any] = field(default_factory=dict) status: str = "open" priority: int = 0 confidence: float = 0.0 risk_level: str = "medium" owner: str | None = None retry_count: int = 0 last_error: str | None = None trigger_reason: str = "" evidence_summary: str = "" draft_skill_name: str | None = None draft_id: str | None = None safety_report_id: str | None = None eval_report_id: str | None = None created_at: str = "" updated_at: str = "" def to_dict(self) -> dict[str, Any]: return { "candidate_id": self.candidate_id, "kind": self.kind, "source_run_ids": list(self.source_run_ids), "source_session_ids": list(self.source_session_ids), "related_skill_names": list(self.related_skill_names), "reason": self.reason, "evidence": dict(self.evidence), "status": self.status, "priority": self.priority, "confidence": self.confidence, "risk_level": self.risk_level, "owner": self.owner, "retry_count": self.retry_count, "last_error": self.last_error, "trigger_reason": self.trigger_reason, "evidence_summary": self.evidence_summary, "draft_skill_name": self.draft_skill_name, "draft_id": self.draft_id, "safety_report_id": self.safety_report_id, "eval_report_id": self.eval_report_id, "created_at": self.created_at, "updated_at": self.updated_at, } @classmethod def from_dict(cls, payload: dict[str, Any]) -> "SkillLearningCandidate": now = _utc_now() status = str(payload.get("status") or "open") risk_level = str(payload.get("risk_level") or "medium") return cls( candidate_id=str(payload["candidate_id"]), kind=str(payload.get("kind") or "revise_skill"), source_run_ids=[str(item) for item in payload.get("source_run_ids") or []], source_session_ids=[str(item) for item in payload.get("source_session_ids") or []], related_skill_names=[str(item) for item in payload.get("related_skill_names") or []], reason=str(payload.get("reason") or ""), evidence=dict(payload.get("evidence") or {}), status=status if status in LEARNING_CANDIDATE_STATUSES else "open", priority=int(payload.get("priority", 0) or 0), confidence=float(payload.get("confidence", 0.0) or 0.0), risk_level=risk_level if risk_level in RISK_LEVELS else "medium", owner=_optional_str(payload.get("owner")), retry_count=int(payload.get("retry_count", 0) or 0), last_error=_optional_str(payload.get("last_error")), trigger_reason=str(payload.get("trigger_reason") or payload.get("reason") or ""), evidence_summary=str(payload.get("evidence_summary") or _summarize_evidence(payload)), draft_skill_name=_optional_str(payload.get("draft_skill_name")), draft_id=_optional_str(payload.get("draft_id")), safety_report_id=_optional_str(payload.get("safety_report_id")), eval_report_id=_optional_str(payload.get("eval_report_id")), created_at=str(payload.get("created_at") or now), updated_at=str(payload.get("updated_at") or payload.get("created_at") or now), ) @dataclass(slots=True) class SkillLearningAuditEvent: event_id: str candidate_id: str event_type: str created_at: str payload: dict[str, Any] = field(default_factory=dict) def to_dict(self) -> dict[str, Any]: return { "event_id": self.event_id, "candidate_id": self.candidate_id, "event_type": self.event_type, "created_at": self.created_at, "payload": dict(self.payload), } @classmethod def from_dict(cls, payload: dict[str, Any]) -> "SkillLearningAuditEvent": return cls( event_id=str(payload["event_id"]), candidate_id=str(payload["candidate_id"]), event_type=str(payload.get("event_type") or ""), created_at=str(payload.get("created_at") or ""), payload=dict(payload.get("payload") or {}), ) @dataclass(slots=True) class SkillDraftSafetyReport: report_id: str skill_name: str draft_id: str passed: bool risk_level: str issues: list[str] = field(default_factory=list) blocked_reasons: list[str] = field(default_factory=list) suggested_fix: str = "" created_at: str = "" def to_dict(self) -> dict[str, Any]: return { "report_id": self.report_id, "skill_name": self.skill_name, "draft_id": self.draft_id, "passed": self.passed, "risk_level": self.risk_level, "issues": list(self.issues), "blocked_reasons": list(self.blocked_reasons), "suggested_fix": self.suggested_fix, "created_at": self.created_at, } @classmethod def from_dict(cls, payload: dict[str, Any]) -> "SkillDraftSafetyReport": risk_level = str(payload.get("risk_level") or "medium") return cls( report_id=str(payload["report_id"]), skill_name=str(payload["skill_name"]), draft_id=str(payload["draft_id"]), passed=bool(payload.get("passed")), risk_level=risk_level if risk_level in RISK_LEVELS else "medium", issues=[str(item) for item in payload.get("issues") or []], blocked_reasons=[str(item) for item in payload.get("blocked_reasons") or []], suggested_fix=str(payload.get("suggested_fix") or ""), created_at=str(payload.get("created_at") or ""), ) @dataclass(slots=True) class SkillDraftEvalReport: report_id: str skill_name: str draft_id: str candidate_id: str passed: bool baseline_score_avg: float candidate_score_avg: float score_delta: float regression_count: int improved_count: int unchanged_count: int cases: list[dict[str, Any]] = field(default_factory=list) status: str = "completed" created_at: str = "" eval_version: str = "heuristic-v1" mode: str = "heuristic" execution_coverage: float = 0.0 surrogate_coverage: float = 0.0 blocked_coverage: float = 0.0 confidence: str = "low" case_reports: list[dict[str, Any]] = field(default_factory=list) tool_mode_summary: dict[str, Any] = field(default_factory=dict) ability_score_summary: dict[str, Any] = field(default_factory=dict) tool_execution_summary: dict[str, Any] = field(default_factory=dict) case_selection_summary: dict[str, Any] = field(default_factory=dict) real_score_avg: float | None = None synthetic_score_avg: float | None = None overall_score_avg: float | None = None preservation_report: dict[str, Any] | None = None def to_dict(self) -> dict[str, Any]: return { "report_id": self.report_id, "skill_name": self.skill_name, "draft_id": self.draft_id, "candidate_id": self.candidate_id, "passed": self.passed, "baseline_score_avg": self.baseline_score_avg, "candidate_score_avg": self.candidate_score_avg, "score_delta": self.score_delta, "regression_count": self.regression_count, "improved_count": self.improved_count, "unchanged_count": self.unchanged_count, "cases": [dict(item) for item in self.cases], "status": self.status, "created_at": self.created_at, "eval_version": self.eval_version, "mode": self.mode, "execution_coverage": self.execution_coverage, "surrogate_coverage": self.surrogate_coverage, "blocked_coverage": self.blocked_coverage, "confidence": self.confidence, "case_reports": [dict(item) for item in self.case_reports], "tool_mode_summary": dict(self.tool_mode_summary), "ability_score_summary": dict(self.ability_score_summary), "tool_execution_summary": dict(self.tool_execution_summary), "case_selection_summary": dict(self.case_selection_summary), "real_score_avg": self.real_score_avg, "synthetic_score_avg": self.synthetic_score_avg, "overall_score_avg": self.overall_score_avg, "preservation_report": ( dict(self.preservation_report) if self.preservation_report is not None else None ), } @classmethod def from_dict(cls, payload: dict[str, Any]) -> "SkillDraftEvalReport": return cls( report_id=str(payload["report_id"]), skill_name=str(payload["skill_name"]), draft_id=str(payload["draft_id"]), candidate_id=str(payload.get("candidate_id") or ""), passed=bool(payload.get("passed")), baseline_score_avg=float(payload.get("baseline_score_avg", 0.0) or 0.0), candidate_score_avg=float(payload.get("candidate_score_avg", 0.0) or 0.0), score_delta=float(payload.get("score_delta", 0.0) or 0.0), regression_count=int(payload.get("regression_count", 0) or 0), improved_count=int(payload.get("improved_count", 0) or 0), unchanged_count=int(payload.get("unchanged_count", 0) or 0), cases=[dict(item) for item in payload.get("cases") or [] if isinstance(item, dict)], status=str(payload.get("status") or "completed"), created_at=str(payload.get("created_at") or ""), eval_version=str(payload.get("eval_version") or "heuristic-v1"), mode=str(payload.get("mode") or "heuristic"), execution_coverage=_bounded_float(payload.get("execution_coverage"), default=0.0), surrogate_coverage=_bounded_float(payload.get("surrogate_coverage"), default=0.0), blocked_coverage=_bounded_float(payload.get("blocked_coverage"), default=0.0), confidence=str(payload.get("confidence") or "low"), case_reports=[ dict(item) for item in payload.get("case_reports") or [] if isinstance(item, dict) ], tool_mode_summary=dict(payload.get("tool_mode_summary") or {}), ability_score_summary=dict(payload.get("ability_score_summary") or {}), tool_execution_summary=dict(payload.get("tool_execution_summary") or {}), case_selection_summary=dict(payload.get("case_selection_summary") or {}), real_score_avg=_optional_bounded_float(payload.get("real_score_avg")), synthetic_score_avg=_optional_bounded_float(payload.get("synthetic_score_avg")), overall_score_avg=_optional_bounded_float(payload.get("overall_score_avg")), preservation_report=( dict(payload["preservation_report"]) if isinstance(payload.get("preservation_report"), dict) else None ), ) def _optional_str(value: Any) -> str | None: if value in (None, ""): return None return str(value) def _optional_bounded_float(value: Any) -> float | None: if value in (None, ""): return None return _bounded_float(value, default=0.0) def _bounded_float(value: Any, *, default: float = 0.0) -> float: if value in (None, ""): return default try: return max(0.0, min(1.0, float(value))) except (TypeError, ValueError): return default def _summarize_evidence(payload: dict[str, Any]) -> str: evidence = payload.get("evidence") if isinstance(evidence, dict): theme = evidence.get("theme") if theme: return f"Theme: {theme}" skill_version = evidence.get("skill_version") if skill_version: return f"Skill version: {skill_version}" source_run_ids = payload.get("source_run_ids") or [] return f"{len(source_run_ids)} source run(s)" def _utc_now() -> str: return datetime.now(timezone.utc).isoformat()