bifroest/app/tasks/states.py
2026-10-27 08:34:17 +01:00

40 lines
1.6 KiB
Python

"""Task-Lebenszyklus (→ Konzept 9.2). Übergänge werden serverseitig geprüft."""
from app.core.errors import Invalid
QUEUED = "queued"
RUNNING = "running"
WAITING_APPROVAL = "waiting_approval"
WAITING_INPUT = "waiting_input"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
BLOCKED_BY_GUARDRAIL = "blocked_by_guardrail"
NEEDS_RECONCILIATION = "needs_reconciliation"
TRANSITIONS: dict[str, set[str]] = {
QUEUED: {RUNNING, CANCELLED},
RUNNING: {QUEUED, WAITING_APPROVAL, WAITING_INPUT, COMPLETED, FAILED, CANCELLED, BLOCKED_BY_GUARDRAIL,
NEEDS_RECONCILIATION},
WAITING_APPROVAL: {QUEUED, CANCELLED},
WAITING_INPUT: {QUEUED, CANCELLED},
NEEDS_RECONCILIATION: {QUEUED, FAILED},
FAILED: {QUEUED},
COMPLETED: set(),
CANCELLED: set(),
BLOCKED_BY_GUARDRAIL: set(),
}
# Zustände, in denen ein Run ohne weiteres Zutun nichts mehr tut (für das Ende eines SSE-Streams)
RESTING = {WAITING_APPROVAL, WAITING_INPUT, COMPLETED, FAILED, CANCELLED, BLOCKED_BY_GUARDRAIL,
NEEDS_RECONCILIATION}
FINAL = {COMPLETED, CANCELLED, BLOCKED_BY_GUARDRAIL}
LABELS = {QUEUED: "wartet", RUNNING: "läuft", WAITING_APPROVAL: "wartet auf Freigabe", WAITING_INPUT: "wartet auf Eingabe",
COMPLETED: "fertig", FAILED: "fehlgeschlagen", CANCELLED: "abgebrochen",
BLOCKED_BY_GUARDRAIL: "blockiert (Guardrail)", NEEDS_RECONCILIATION: "Abgleich nötig"}
def check(current: str, target: str) -> None:
if target not in TRANSITIONS[current]:
raise Invalid(f"Übergang {current} → {target} ist nicht erlaubt.")