Files
openswarm/backend/apps/workflows/notifier.py
T

54 lines
2.2 KiB
Python

"""Permission/escalation chain notifier.
The notify tier broadcasts a ws event the renderer picks up. The text/call
tiers route through the cloud SMS bridge once enabled; until it's enabled
we fall back to an extra ws notify with a `fallback: true` marker so the
renderer can label it honestly ("Text-me fallback: cloud SMS not wired").
The *when* of escalation is owned by apps/workflows/escalation.py.
"""
import logging
from datetime import datetime
from backend.apps.workflows.models import PermissionTier, Workflow, WorkflowRun
logger = logging.getLogger(__name__)
def _base_payload(wf: Workflow, run: WorkflowRun) -> dict:
return {
"workflow_id": wf.id,
"workflow_title": wf.title,
"run_id": run.id,
"status": run.status,
"session_id": run.session_id,
"started_at": run.started_at.isoformat() if isinstance(run.started_at, datetime) else run.started_at,
"finished_at": run.finished_at.isoformat() if isinstance(run.finished_at, datetime) else run.finished_at,
}
async def notify_run_complete(wf: Workflow, run: WorkflowRun) -> None:
from backend.apps.agents.core.ws_manager import ws_manager
from backend.apps.workflows import escalation
payload = _base_payload(wf, run)
await ws_manager.broadcast_global("workflow:notify", payload)
# Kick off server-side escalation only if there are additional tiers beyond the default notify. The escalation runner will sleep + call send_tier per tier.
escalation.schedule(wf, run)
async def send_tier(wf: Workflow, run: WorkflowRun, tier: PermissionTier) -> None:
"""Send a single escalation tier. Today the text/call paths fall back
to an in-app notify with `fallback: true` and the tier kind set so the
renderer can show "Text-me fallback (cloud SMS not wired)."
"""
from backend.apps.agents.core.ws_manager import ws_manager
payload = _base_payload(wf, run)
payload["tier_kind"] = tier.kind
payload["tier_phone"] = (tier.phone or "")[-4:] if tier.phone else None
payload["fallback"] = True # flip to False once the cloud SMS bridge is wired
await ws_manager.broadcast_global("workflow:notify", payload)
logger.info("workflow tier=%s fallback fired wf=%s run=%s", tier.kind, wf.id, run.id)