Spaces:
Sleeping
Sleeping
| """Telemetry helper for anonymized safety events. | |
| This module provides a minimal notifier that logs low-score / refused | |
| events and optionally POSTs anonymized metadata to a configured webhook. | |
| No secrets are sent. | |
| """ | |
| import os | |
| import json | |
| import logging | |
| import re | |
| from typing import Mapping, Any | |
| def notify_low_score(event: Mapping[str, Any]) -> None: | |
| """Notify about a low-score or refused safety event. | |
| event: mapping with non-sensitive metadata, e.g. { | |
| "kind": "prompt" | "answer", | |
| "score": 12.3, | |
| "detectors": ["jailbreak", "base64"], | |
| "session_id_present": False, | |
| } | |
| """ | |
| logger = logging.getLogger("telemetry") | |
| try: | |
| logger.info("Telemetry event: %s", json.dumps(event)) | |
| except Exception: | |
| logger.info("Telemetry event: %s", str(event)) | |
| webhook = os.getenv("TELEMETRY_WEBHOOK") | |
| if not webhook: | |
| return | |
| # Only POST refused events to the external telemetry webhook. | |
| # Allowed refused reasons: | |
| refused_reasons = { | |
| "sensitive_operation", | |
| "moderation_flagged", | |
| "safeguard_model", | |
| "response_refused", | |
| "refused", | |
| } | |
| reason = str(event.get("reason", "")).lower() if event else "" | |
| score = None | |
| try: | |
| score = float(event.get("score")) if event and "score" in event else None | |
| except Exception: | |
| score = None | |
| # Post only when explicitly refused (reason in refused_reasons) or score==0.0 | |
| should_post = False | |
| if reason and any(r in reason for r in refused_reasons): | |
| should_post = True | |
| if score is not None and score <= 0.0: | |
| should_post = True | |
| if not should_post: | |
| logger.info( | |
| "Telemetry event ignored (not a refused event): %s", json.dumps(event) | |
| ) | |
| return | |
| payload = {"event": event} | |
| try: | |
| import requests | |
| headers = {"Content-Type": "application/json"} | |
| # POST anonymized metadata only | |
| requests.post(webhook, json=payload, headers=headers, timeout=2) | |
| except Exception as e: # pragma: no cover - network | |
| logger.debug("Telemetry post failed: %s", e) | |
| def make_snippet(text: str, max_chars: int = 120) -> str: | |
| """Create a short, single-line snippet suitable for logs and UI. | |
| - strips control characters and collapses whitespace | |
| - truncates with an ellipsis if longer than `max_chars` | |
| """ | |
| if not text: | |
| return "" | |
| try: | |
| s = str(text) | |
| except Exception: | |
| return "" | |
| # replace control characters with a single space to avoid word concatenation | |
| s = re.sub(r"[\u0000-\u001F\u007F]+", " ", s) | |
| # collapse whitespace/newlines | |
| s = re.sub(r"\s+", " ", s).strip() | |
| if len(s) <= max_chars: | |
| return s | |
| # truncate safely | |
| return s[: max_chars - 1].rstrip() + "…" | |