#!/usr/bin/env python3 """Pure execution-target policy for Agent Task worker and review stages.""" from __future__ import annotations from dataclasses import dataclass from datetime import datetime, time from zoneinfo import ZoneInfo KST = ZoneInfo("Asia/Seoul") DAY_START = time(7, 0) NIGHT_START = time(23, 0) VALID_STAGES = {"worker", "review"} VALID_LANES = {"local", "cloud"} @dataclass(frozen=True) class RouteTarget: adapter: str target: str execution_class: str selfcheck_required: bool @dataclass(frozen=True) class PolicyDecision: rule_id: str policy_priority: int reason_codes: tuple[str, ...] time_window: str candidates: tuple[RouteTarget, ...] PI_ORNITH = RouteTarget("pi", "iop/ornith-fast", "local_model", True) PI_LAGUNA = RouteTarget("pi", "iop/laguna-s:2.1", "local_model", True) AGY_GEMINI_MEDIUM = RouteTarget( "agy", "Gemini 3.6 Flash Medium", "cloud_model", False ) CLAUDE_SONNET = RouteTarget("claude", "sonnet", "cloud_model", False) CLAUDE_OPUS = RouteTarget("claude", "claude-opus-4-8", "cloud_model", False) CODEX_SOL_XHIGH = RouteTarget("codex", "gpt-5.6-sol", "cloud_model", False) def _validate(stage: str, lane: str, grade: int, evaluated_at: datetime) -> None: if stage not in VALID_STAGES: raise ValueError(f"unsupported stage: {stage}") if lane not in VALID_LANES: raise ValueError(f"unsupported lane: {lane}") if not 1 <= grade <= 10: raise ValueError(f"grade must be in G01..G10: {grade}") if evaluated_at.tzinfo is None or evaluated_at.utcoffset() is None: raise ValueError("evaluated_at must be timezone-aware") def _kst_window(evaluated_at: datetime) -> tuple[str, bool]: current = evaluated_at.astimezone(KST).time().replace(tzinfo=None) is_day = DAY_START <= current < NIGHT_START if is_day: return "kst-day-[07:00,23:00)", True return "kst-night-[23:00,07:00)", False def select_policy( *, stage: str, lane: str, grade: int, evaluated_at: datetime ) -> PolicyDecision: """Return the ordered target policy for one initial route evaluation.""" _validate(stage, lane, grade, evaluated_at) if stage == "review": return PolicyDecision( rule_id="official-review-codex", policy_priority=10, reason_codes=("official_review_fixed",), time_window="not_applicable", candidates=(CODEX_SOL_XHIGH,), ) if lane == "local": if grade <= 6: return PolicyDecision( rule_id="worker-local-g01-g06", policy_priority=30, reason_codes=("local_low_grade",), time_window="not_applicable", candidates=(PI_ORNITH,), ) if grade <= 8: window, is_day = _kst_window(evaluated_at) return PolicyDecision( rule_id=( "worker-local-g07-g08-day" if is_day else "worker-local-g07-g08-night" ), policy_priority=20, reason_codes=( "kst_day_window" if is_day else "kst_night_window", ), time_window=window, candidates=( (AGY_GEMINI_MEDIUM, PI_LAGUNA) if is_day else (PI_LAGUNA, AGY_GEMINI_MEDIUM) ), ) return PolicyDecision( rule_id="worker-local-g09-g10", policy_priority=30, reason_codes=("local_high_grade_cloud_target",), time_window="not_applicable", candidates=(CLAUDE_OPUS,), ) if grade <= 6: target = CLAUDE_SONNET rule_id = "worker-cloud-g01-g06" reason_code = "cloud_sonnet_grade" elif grade <= 8: target = CLAUDE_OPUS rule_id = "worker-cloud-g07-g08" reason_code = "cloud_opus_grade" else: target = CODEX_SOL_XHIGH rule_id = "worker-cloud-g09-g10" reason_code = "cloud_codex_grade" return PolicyDecision( rule_id=rule_id, policy_priority=30, reason_codes=(reason_code,), time_window="not_applicable", candidates=(target,), )