"""Model routing per task. Professor overrides (DB) take precedence over env config.""" from __future__ import annotations from dataclasses import dataclass, field from typing import Any from app.core.config import Settings, get_settings @dataclass class RoutePlan: models: list[str] temperature: float reasoning: dict[str, Any] | None = None response_format: dict[str, Any] | None = None extra: dict[str, Any] = field(default_factory=dict) class ModelRouter: def __init__(self, settings: Settings | None = None) -> None: self.s = settings or get_settings() self.overrides: dict[str, str] = {} # e.g. {"primary": "anthropic/claude-opus-4.6"} self.budget_exceeded = False def primary(self) -> str: return self.overrides.get("primary") or self.s.MODEL_TUTOR_PRIMARY def plan(self, task: str, deep: bool = False) -> RoutePlan: s = self.s if self.budget_exceeded and task in {"tutor", "reasoning", "vision", "quiz"}: return RoutePlan([s.MODEL_FAST], 0.3) if (task == "tutor" and deep) or task == "reasoning": return RoutePlan([s.MODEL_REASONING, self.primary()], 0.2, {"effort": "high"}) if task == "tutor": return RoutePlan([self.primary(), s.MODEL_TUTOR_FALLBACK], 0.3) if task == "fast": return RoutePlan([s.MODEL_FAST], 0.0) if task == "vision": return RoutePlan([s.MODEL_VISION, s.MODEL_TUTOR_FALLBACK], 0.2) if task == "quiz": return RoutePlan([self.primary(), s.MODEL_TUTOR_FALLBACK], 0.4, response_format={"type": "json_object"}) return RoutePlan([self.primary(), s.MODEL_TUTOR_FALLBACK], 0.3) router = ModelRouter()