Self-hosted, Docker-based agentic troubleshooting platform: FastAPI backend + LangGraph agent, Next.js UI, tiered LLM routing (local Ollama -> Gemini -> DeepSeek -> OpenRouter), MCP server manager, encrypted device credentials, RBAC, audit log, project-memory + Obsidian integrations, and editable troubleshooting decision rules tuned for the GeneseasX vessel stack. Co-authored-by: Cursor <cursoragent@cursor.com>
65 lines
1.9 KiB
Python
65 lines
1.9 KiB
Python
"""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]
|