mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
81 lines
2.7 KiB
Python
81 lines
2.7 KiB
Python
"""Unified HITL (human-in-the-loop) approval flow.
|
|
|
|
Used by both the main agent loop and browser sub-agents.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from datetime import datetime
|
|
from uuid import uuid4
|
|
|
|
from backend.apps.agents.models import AgentSession, ApprovalRequest
|
|
from backend.apps.agents.manager.ws_manager import ws_manager
|
|
|
|
from backend.apps.analytics.collector import record as _analytics
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def request_approval(
|
|
session: AgentSession,
|
|
tool_name: str,
|
|
tool_input: dict,
|
|
timeout: float | None = None,
|
|
track_analytics: bool = True,
|
|
) -> dict:
|
|
"""Unified HITL approval flow.
|
|
|
|
Creates an ApprovalRequest, sends it via WebSocket, waits for the user's
|
|
decision, cleans up, and returns the decision dict.
|
|
|
|
Returns: {"behavior": "allow"|"deny", "message": ..., "updated_input": ...}
|
|
"""
|
|
safe_input = tool_input if isinstance(tool_input, dict) else {}
|
|
request_id = uuid4().hex
|
|
approval_req = ApprovalRequest(
|
|
id=request_id, session_id=session.id,
|
|
tool_name=tool_name, tool_input=safe_input,
|
|
)
|
|
session.pending_approvals.append(approval_req)
|
|
session.status = "waiting_approval"
|
|
|
|
if track_analytics:
|
|
_analytics("approval.requested", {
|
|
"tool_name": tool_name,
|
|
"is_first_approval_in_session": len(session.pending_approvals) == 1,
|
|
"model": session.model,
|
|
}, session_id=session.id, dashboard_id=session.dashboard_id)
|
|
|
|
await ws_manager.emit_status(session.id, "waiting_approval")
|
|
|
|
if timeout is not None:
|
|
try:
|
|
decision = await asyncio.wait_for(
|
|
ws_manager.send_approval_request(session.id, request_id, tool_name, safe_input),
|
|
timeout=timeout,
|
|
)
|
|
except asyncio.TimeoutError:
|
|
decision = {"behavior": "deny", "message": "Approval timed out"}
|
|
else:
|
|
decision = await ws_manager.send_approval_request(
|
|
session.id, request_id, tool_name, safe_input,
|
|
)
|
|
|
|
if track_analytics:
|
|
latency_ms = int((datetime.now() - approval_req.created_at).total_seconds() * 1000)
|
|
_analytics("approval.resolved", {
|
|
"tool_name": tool_name,
|
|
"decision": decision.get("behavior", "unknown"),
|
|
"latency_ms": latency_ms,
|
|
"input_was_modified": decision.get("updated_input") is not None,
|
|
"model": session.model,
|
|
}, session_id=session.id, dashboard_id=session.dashboard_id)
|
|
|
|
session.pending_approvals = [a for a in session.pending_approvals if a.id != request_id]
|
|
session.status = "running"
|
|
await ws_manager.emit_status(session.id, "running")
|
|
return decision
|