| from __future__ import annotations |
|
|
| import json |
| import re |
| from pathlib import Path |
| from typing import Any |
|
|
| _REDACT_KEYS = { |
| "api_key", |
| "authorization", |
| "cohere_api_key", |
| "hf_token", |
| "name", |
| "password", |
| "secret", |
| "situation", |
| "token", |
| "user_name", |
| } |
|
|
|
|
| def _sensitive_values(value: Any) -> tuple[str, ...]: |
| found: set[str] = set() |
| if isinstance(value, dict): |
| for item_key, item_value in value.items(): |
| if ( |
| item_key.casefold() in {"name", "situation", "user_name"} |
| and isinstance(item_value, str) |
| and item_value.strip() |
| ): |
| found.add(item_value.strip()) |
| found.update(_sensitive_values(item_value)) |
| elif isinstance(value, (list, tuple)): |
| for item in value: |
| found.update(_sensitive_values(item)) |
| return tuple(sorted(found, key=len, reverse=True)) |
|
|
|
|
| def sanitize_trace( |
| value: Any, |
| key: str = "", |
| sensitive_values: tuple[str, ...] | None = None, |
| ) -> Any: |
| if sensitive_values is None: |
| sensitive_values = _sensitive_values(value) |
| normalized_key = key.casefold() |
| if normalized_key in _REDACT_KEYS or any( |
| marker in normalized_key for marker in ("token", "api_key", "password", "secret") |
| ): |
| return "[redacted]" |
| if isinstance(value, dict): |
| return { |
| item_key: sanitize_trace(item_value, item_key, sensitive_values) |
| for item_key, item_value in value.items() |
| } |
| if isinstance(value, list): |
| return [sanitize_trace(item, sensitive_values=sensitive_values) for item in value] |
| if isinstance(value, tuple): |
| return [sanitize_trace(item, sensitive_values=sensitive_values) for item in value] |
| if isinstance(value, str): |
| sanitized = value |
| for sensitive in sensitive_values: |
| sanitized = re.sub( |
| re.escape(sensitive), |
| "[redacted]", |
| sanitized, |
| flags=re.IGNORECASE, |
| ) |
| return sanitized |
| return value |
|
|
|
|
| class TraceRecorder: |
| def __init__(self, path: str | Path) -> None: |
| self.path = Path(path) |
|
|
| def append(self, record: dict[str, Any]) -> None: |
| self.path.parent.mkdir(parents=True, exist_ok=True) |
| safe_record = sanitize_trace(record) |
| with self.path.open("a", encoding="utf-8") as trace_file: |
| trace_file.write(json.dumps(safe_record, ensure_ascii=True) + "\n") |
|
|