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 7157cba
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
+
This addendum records a deliberate change to the original "metadata only" data-handling
386
+
stance for a **narrow, named set of spans**, the follow-on span-enrichment work it implies,
387
+
and the PII-handling strategy that must accompany it. It supersedes the relevant parts of
388
+
**R7** and the "Safe observability by design" bullet (§Why) **for the three core inference
389
+
spans only** (`/v1/query`, `/v1/streaming_query`, `/v1/responses`). All other spans continue
390
+
to follow the original metadata-only rule.
391
+
392
+
### A1. What changed and why
393
+
394
+
The original design captured **structured metadata only** — IDs, counts, lengths, coarse
395
+
results — and explicitly avoided raw prompts and retrieved content (R7; §Why bullet 4;
396
+
§Failure handling → "Metadata only... No raw prompts or retrieved content"). Content-bearing
397
+
fields that were emitted at all (`request.input`, `response.output`, `feedback.comment`, …)
398
+
were passed through `anonymize_value()` — an HMAC-SHA-256 digest (first 64 bits) plus a length
399
+
tag, e.g. `[hash:ab12…:long:len=412]`.
400
+
401
+
[LCORE-3755](https://redhat.atlassian.net/browse/LCORE-3755) introduces a conflicting,
402
+
legitimate requirement: an ML engineer building a **RAGAS / DeepEval** evaluation harness needs
403
+
the **raw, un-hashed** input/response pairs — plus RAG chunk and tool-use detail — from the
404
+
core inference spans in order to compute metrics such as faithfulness, context precision/recall,
405
+
and tool-call correctness. A hash carries none of that signal: you cannot score a digest.
406
+
407
+
This addendum therefore:
408
+
409
+
1.**Removes `anonymize_value()` from content fields** on the covered endpoints (input, output,
410
+
RAG input, feedback comment, and the A2A request id), while **keeping** it for pure identity
411
+
fields (`user.id`). `safety_identifier` continues to be recorded verbatim — it is OpenAI's
412
+
documented opaque, non-PII caller identifier.
413
+
2. Establishes the **richer eval attributes** LCORE-3755 asks for as planned follow-on work
414
+
(§A3).
415
+
3. Establishes a **replacement PII strategy** — detect-and-redact via the [shared Presidio library](https://docs.google.com/document/d/1BNBIDUz-lLmUjT9N9cEMqgaQv9EqfLKwZrA8gtbtlY8/edit?tab=t.0#heading=h.9qfphopokr7u) being currently used by some lightspeed teams (eg Ask RedHat)
416
+
### A2. LCORE-3755 gap analysis and remaining work
417
+
418
+
[PR#2731](https://github.com/lightspeed-core/lightspeed-stack/pull/2731) satisfies the raw request/response criteria. The remaining
419
+
criteria are net-new instrumentation, tracked as follow-on work under LCORE-3755.
| 1 |`conversation_id` on all 3 core spans | Present | emitted today as `session.id` — confirm naming vs. `conversation_id`|
424
+
| 2 |`request` raw / un-hashed on all 3 |**Done (PR#2731)**|`request.input` now raw |
425
+
| 3 |`response` raw / un-hashed on all 3 |**Done (slice 1)**|`response.output` now raw (incl. streaming path) |
426
+
| 10 |`input_tokens` / `output_tokens` on all 3 | Present | already set on query, streaming, responses |
427
+
| 7 |`model` name on all 3 | Partial | present on responses + non-streaming query; **missing on the streaming root span**|
428
+
| 8 |`inference_time` / `latency` on all 3 | Partial | only implicit **span duration** today; decide whether an explicit attribute is required |
429
+
| 11 | Shield **decision + reason** on all 3 | Partial |`shield.result` = `passed`/`blocked` on the child `shield.moderate` span; **no reason**, not on the root spans |
430
+
| 4 |`rag_chunks` full list (content, source, score, attributes), inline + tool-based |**Net-new**| today only `rag.sources.count` + `rag.sources` = **doc URLs**; no content/score/attributes; nothing for `file_search_call` chunks |
431
+
| 5 |`tool_calls` full detail (id, name, args) |**Net-new**| today only `tool.calls.count` / `tool.calls.names`|
432
+
| 6 |`tool_results` full detail (id, status, content, round) |**Net-new (emission only)**|`id`/`status`/`content`/`round` already exist on `ToolResultSummary`; only the span emission is missing |
433
+
| 12 |`round` on tool results, both paths |**Net-new (emission only)**| already a field on `ToolResultSummary`; net-new is emitting it (and keeping the streaming vs non-streaming paths in agreement) |
434
+
| 13 | Unit tests for each of the above |**Net-new**||
435
+
436
+
**Design considerations for the net-new work:**
437
+
438
+
#### This is additive raw content.
439
+
440
+
Every net-new field above (chunk content, tool args, tool results) is *additional* PII surface beyond
441
+
input/output — it depends on §A4 being in place.
442
+
443
+
#### Attribute encoding for structured values
444
+
445
+
OpenTelemetry allows an attribute value to be only a primitive (`str`, `bool`, `int`, `float`)
446
+
or a **homogeneous** sequence of primitives — no maps, no lists of objects. And `span.set_attribute()`
447
+
does **not** raise on a bad type: the SDK logs a warning once and **silently drops the attribute**.
448
+
So handing it a list of chunk objects yields a span with no data and no error, unnoticed until the
449
+
eval harness comes up empty.
450
+
451
+
That rules out setting the structured fields (`rag_chunks`, `tool_calls`, `tool_results`) as native attributes.
452
+
453
+
**Approach — scalars native, collections as one JSON string each.** We will start with the most
454
+
straightforward encoding that fits the above mentioned constraint: emit the scalar fields as native attributes,
455
+
and serialize each collection to a single JSON-string attribute (`json.dumps` of the list).
456
+
457
+
| Field | Encoding | Type |
458
+
|---|---|---|
459
+
|`llm.latency_ms`| native attribute |`float` (unit encoded in the key) |
0 commit comments