You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 93c08c8
Browse filesBrowse the repository at this point in the historyBrowse files
|**Feature / Initiative**|[UIESTRAT-229: Enable collection & upload of Observability OTel data stripped of PII/sensitive data](https://redhat.atlassian.net/browse/UIESTRAT-229)|
384
+
385
+
The three core inference spans (`/v1/query`, `/v1/streaming_query`, `/v1/responses`) carry
386
+
raw, un-hashed request/response content for evaluation, relaxing the original metadata-only rule
387
+
(**R7**; §Why "Safe observability by design"). This addendum covers how that content is
388
+
redacted of PII.
389
+
390
+
### The problem
391
+
392
+
Raw span text (prompts, responses, RAG chunks, tool I/O) can contain PII, so it must be scrubbed
393
+
before the span leaves LCORE — before OTLP export to a hosted backend such as LangFuse, and before
394
+
any cross-org sharing. This is a portfolio-wide obligation from Red Hat's AI Assessment (AIA/PIA)
395
+
process, not specific to LCORE.
396
+
397
+
### The approach — Presidio from PyPI
398
+
399
+
Use the upstream Presidio library directly. Add `presidio-analyzer` and `presidio-anonymizer`
400
+
(both on PyPI) to `pyproject.toml`, and wrap them in a small `redact(text) -> str` helper that
401
+
detects and replaces email, hostname, IP (v4/v6), location, organization, person, phone, and URL.
402
+
No new infrastructure — a dependency plus one helper, applied wherever content is set on a span
403
+
(next section).
404
+
405
+
Note: Presidio needs a spaCy model for named-entity detection, which is a sizable download.
406
+
407
+
### Where redaction runs — redact at emission, through one helper
408
+
409
+
Content is redacted at the point it's set on the span. All content fields will go on spans
410
+
through a **single centralized helper** — e.g. `set_content_attribute(span, key, text)` — that runs
411
+
each value through the Presidio `redact()` helper before calling `set_attribute`. For any content
412
+
serialized as a JSON string (e.g. RAG chunks or tool I/O), the helper redacts each leaf string
413
+
(`content`/`args`/`source`) before `json.dumps`, so the serialized attribute never contains raw
414
+
PII. Properties of this approach:
415
+
416
+
-**The span never holds raw PII.** R11 ("redact before content leaves the process") is satisfied
417
+
by construction — there is no in-memory window where a raw value sits on a span or in the export
418
+
queue.
419
+
-**One code path to audit.** Because content can only reach a span through the helper, there is a
420
+
single place to review and test; individual emission sites cannot forget to redact (there is no
421
+
raw path to `set_attribute` for content keys). A review/lint rule reinforces "content goes
422
+
through the helper."
423
+
-**Fail-closed.** If the redactor raises, the helper omits the field rather than emitting raw
424
+
text — consistent with R9 (a tracing failure must never leak or break the request).
425
+
-**Cost.** Redaction runs on the request path (synchronous), not in the export background thread.
426
+
This is minor. It's a text pass over already-generated output, dwarfed by the LLM call, and only
427
+
incurred when raw-content capture is enabled (scoped to the three core spans). The helper is the
428
+
single place to make it async/best-effort if it ever becomes a bottleneck.
429
+
430
+
### Future: adopting the shared `data-anonymizer` library
431
+
432
+
A shared, Presidio-based redactor already exists: `data-anonymizer` (used by Ask Red Hat et al.;
433
+
see the [shared Presidio library strategy doc](https://docs.google.com/document/d/1BNBIDUz-lLmUjT9N9cEMqgaQv9EqfLKwZrA8gtbtlY8/edit?tab=t.0#heading=h.9qfphopokr7u)).
434
+
LCORE may move to it eventually, so redaction is maintained once across teams rather than
435
+
per-project. The only obstacle is distribution. `data-anonymizer` lives on Red Hat's internal GitLab
436
+
(`gitlab.cee.redhat.com/uxe-data-ai-solutions/data-anonymizer`), not PyPI, and a GitHub build/CI
437
+
can't install from internal GitLab without internal credentials — so it can't go in
438
+
`pyproject.toml`.
439
+
440
+
The swap stays small because redaction is already funneled through the single `redact()` helper
441
+
(§ Where redaction runs). To adopt the shared library without breaking the GitHub build, turn that
442
+
helper into a **redaction slot** — one interface the telemetry path calls without knowing the
443
+
backend:
444
+
445
+
-**GitHub build (default):** the slot keeps using public Presidio from PyPI.
446
+
-**Red Hat's internal build:** a downstream build fills the slot with `data-anonymizer` via an
447
+
adapter, added at the stage where internal GitLab is reachable.
448
+
449
+
Until such a move is deemed required, the PyPI Presidio helper above is the whole
450
+
implementation.
451
+
452
+
### Requirements addendum
453
+
454
+
-**R11 — PII redaction before export.** Raw content on the three core inference spans shall pass
455
+
through PII detection/redaction before leaving the process (OTLP export or cross-org sharing).
0 commit comments