from __future__ import annotations from contextvars import ContextVar, Token from copy import deepcopy from datetime import datetime, timezone import re import threading import time import uuid from typing import Any, Dict, Optional _OPERATION_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]{8,80}$") _OPERATION_TTL_SECONDS = 15 * 60 _MAX_OPERATIONS = 500 _MAX_EVENTS = 60 _current_operation_id: ContextVar[Optional[str]] = ContextVar( "magent_operation_id", default=None ) _operations: Dict[str, Dict[str, Any]] = {} _lock = threading.Lock() def _now_iso() -> str: return datetime.now(timezone.utc).isoformat() def normalize_operation_id(value: Optional[str]) -> Optional[str]: if not isinstance(value, str): return None normalized = value.strip() return normalized if _OPERATION_ID_PATTERN.fullmatch(normalized) else None def _prune_locked(now_monotonic: float) -> None: expired = [ operation_id for operation_id, operation in _operations.items() if now_monotonic - float(operation.get("updated_monotonic") or 0) > _OPERATION_TTL_SECONDS ] for operation_id in expired: _operations.pop(operation_id, None) if len(_operations) <= _MAX_OPERATIONS: return oldest = sorted( _operations, key=lambda operation_id: float(_operations[operation_id].get("updated_monotonic") or 0), ) for operation_id in oldest[: len(_operations) - _MAX_OPERATIONS]: _operations.pop(operation_id, None) def begin_operation(operation_id: str, *, label: Optional[str], path: str) -> Token: now_monotonic = time.monotonic() now_iso = _now_iso() normalized_label = str(label or "Requested action").strip()[:120] or "Requested action" with _lock: _prune_locked(now_monotonic) _operations[operation_id] = { "id": operation_id, "label": normalized_label, "path": path, "status": "running", "started_at": now_iso, "updated_at": now_iso, "updated_monotonic": now_monotonic, "duration_ms": None, "events": [ { "id": uuid.uuid4().hex, "service": "Magent", "state": "complete", "message": "Your action has been received. Magent is starting the checks.", "started_at": now_iso, "finished_at": now_iso, "duration_ms": 0, "status_code": None, } ], } return _current_operation_id.set(operation_id) def reset_operation(token: Token) -> None: _current_operation_id.reset(token) def start_remote_call(service: str, message: Optional[str] = None) -> Optional[str]: operation_id = _current_operation_id.get() if not operation_id: return None event_id = uuid.uuid4().hex now_iso = _now_iso() now_monotonic = time.monotonic() with _lock: operation = _operations.get(operation_id) if not operation: return None operation["events"].append( { "id": event_id, "service": service, "state": "active", "message": message or f"Contacting {service}…", "started_at": now_iso, "finished_at": None, "duration_ms": None, "status_code": None, "started_monotonic": now_monotonic, } ) operation["events"] = operation["events"][-_MAX_EVENTS:] operation["updated_at"] = now_iso operation["updated_monotonic"] = now_monotonic return event_id def finish_remote_call( event_id: Optional[str], *, success: bool, status_code: Optional[int] = None, message: Optional[str] = None, ) -> None: operation_id = _current_operation_id.get() if not operation_id or not event_id: return now_iso = _now_iso() now_monotonic = time.monotonic() with _lock: operation = _operations.get(operation_id) if not operation: return event = next( (candidate for candidate in operation["events"] if candidate.get("id") == event_id), None, ) if not event: return started_monotonic = float(event.pop("started_monotonic", now_monotonic)) event["state"] = "complete" if success else "error" event["finished_at"] = now_iso event["duration_ms"] = round((now_monotonic - started_monotonic) * 1000, 1) event["status_code"] = status_code event["message"] = message or ( f"{event['service']} responded successfully." if success else f"{event['service']} returned an error." ) operation["updated_at"] = now_iso operation["updated_monotonic"] = now_monotonic def finish_operation(operation_id: str, *, success: bool, status_code: Optional[int]) -> None: now_iso = _now_iso() now_monotonic = time.monotonic() with _lock: operation = _operations.get(operation_id) if not operation: return for event in operation["events"]: if event.get("state") == "active": started_monotonic = float(event.pop("started_monotonic", now_monotonic)) event["state"] = "error" event["finished_at"] = now_iso event["duration_ms"] = round((now_monotonic - started_monotonic) * 1000, 1) event["message"] = f"{event.get('service') or 'Remote service'} did not complete." started = datetime.fromisoformat(str(operation["started_at"])) duration_ms = (datetime.now(timezone.utc) - started).total_seconds() * 1000 operation["status"] = "complete" if success else "error" operation["status_code"] = status_code operation["duration_ms"] = round(duration_ms, 1) operation["updated_at"] = now_iso operation["updated_monotonic"] = now_monotonic operation["events"].append( { "id": uuid.uuid4().hex, "service": "Magent", "state": "complete" if success else "error", "message": ( "This action has finished. Check the request status for what happens next." if success else "This action could not be completed. Open the activity details to see which step needs attention." ), "started_at": now_iso, "finished_at": now_iso, "duration_ms": 0, "status_code": status_code, } ) operation["events"] = operation["events"][-_MAX_EVENTS:] def get_operation(operation_id: str) -> Optional[Dict[str, Any]]: normalized = normalize_operation_id(operation_id) if not normalized: return None with _lock: operation = _operations.get(normalized) if not operation: return None result = deepcopy(operation) result.pop("updated_monotonic", None) for event in result.get("events", []): event.pop("started_monotonic", None) return result