"""Per-task runtime flags (Redis) — e.g. user-requested LLM tier escalation and cancellation.""" from __future__ import annotations from app.services.events import get_redis TIER_ORDER = ["local", "economy", "premium"] TIER_RANK = {t: i for i, t in enumerate(TIER_ORDER)} CANCELLABLE_STATUSES = frozenset({"queued", "running", "waiting_approval"}) class TaskCancelledError(Exception): """Raised when the user stops a running or queued task.""" def _min_tier_key(task_id: int) -> str: return f"agentic:task:{task_id}:min_tier" def _cancel_key(task_id: int) -> str: return f"agentic:task:{task_id}:cancel" async def request_task_cancel(task_id: int) -> None: await get_redis().set(_cancel_key(task_id), "1", ex=86400) async def is_task_cancel_requested(task_id: int) -> bool: return bool(await get_redis().get(_cancel_key(task_id))) async def clear_task_cancel(task_id: int) -> None: await get_redis().delete(_cancel_key(task_id)) async def ensure_not_cancelled(task_id: int) -> None: if await is_task_cancel_requested(task_id): raise TaskCancelledError("Task cancelled by user") async def set_task_min_tier(task_id: int, tier: str) -> None: if tier not in TIER_RANK: raise ValueError(f"Invalid tier: {tier}") await get_redis().set(_min_tier_key(task_id), tier, ex=86400) async def get_task_min_tier(task_id: int) -> str | None: tier = await get_redis().get(_min_tier_key(task_id)) return tier if tier in TIER_RANK else None async def clear_task_min_tier(task_id: int) -> None: await get_redis().delete(_min_tier_key(task_id)) def apply_min_tier_floor(tiers: list[str], min_tier: str) -> list[str]: """Drop tiers below min_tier (e.g. skip local when user requests economy).""" if min_tier not in TIER_RANK: return tiers floor = TIER_RANK[min_tier] filtered = [t for t in tiers if TIER_RANK[t] >= floor] if filtered: return filtered return [min_tier]