rags_api / telemetry.py
Skier8402's picture
Upload 3 files
cdb228e verified
"""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() + "…"