Repository navigation
fix(livekit): keep the Future AGI exporter when LiveKit adds its privacy filter (TH-8665) - #273
Merged
Merged
Conversation
…acy filter (TH-8665) With livekit-agents 1.8, traceAI-livekit sent no traces. register() marks its own exporter as a default that the next add_span_processor call replaces, and LiveKit's set_tracer_provider adds its privacy filter through add_span_processor, so the exporter was shut down and removed. enable_*_attribute_mapping() now clears that mark before handing the provider to LiveKit, so LiveKit's filter and the exporter both stay. fi_instrumentation and other frameworks' register() are unchanged. Release: traceAI-livekit 0.1.2 (version, __version__, a new CHANGELOG). Not published. The README says to let enable_* hand the provider to LiveKit, and how to keep conversation content out of traces. Signed-off-by: khushalsonawat <[email protected]>
…own dev setup (TH-8665) The dev group now pins livekit-agents and its two plugins to 1.8.x (Python 3.10+, which 1.8 needs) and adds pytest, so tests/test_integration.py runs after `poetry install` instead of skipping on the old 1.3.3 lock. Signed-off-by: khushalsonawat <[email protected]>
9 of 10 tasks
… never fail the mapping silently (TH-8665)
Since livekit-agents 1.7, LiveKit names its content attributes lk.pii.*
(lk.pii.user_input, lk.pii.chat_ctx, lk.pii.response.text, ...). The exporter
matched only the old names, so voice spans arrived with no input.value or
output.value. Both names are now read, the newer first. An empty function-call
list ("[]", set on every LLM node) is no longer output, so the reply alone is.
A mapping failure used to be swallowed; it is now logged once per process,
without attribute values, and the span is still exported. The
fi-instrumentation-otel floor goes from 0.1.14 to 1.1.0: before 1.0.0 the
attribute names the mapping uses are missing, and 1.0.0 fails inside
register() with OpenTelemetry 1.45 (livekit-agents 1.8.7+).
README gains a Compatibility section and a link to the attribute-name mapping;
the set_tracer_provider note now says a call after enable_* is safe. Tests:
pure mapping on both name sets, an end-to-end agent turn through the real
exporter to a local collector, the LIVEKIT_TELEMETRY_ALLOW_PII=false opt-out,
and LiveKit's module tracer restored after each test.
Signed-off-by: khushalsonawat <[email protected]>
khushalsonawat
commented
Oct 19, 2026
khushalsonawat
left a comment
Member
Author
There was a problem hiding this comment.
One High: the exporter rewrites the shared span, so the newly mapped lk.pii.* content reaches LiveKit Cloud under keys its redaction doesn't strip. The rest holds: the mapping on real 1.8 spans, the old names, the warning and the 1.1.0 floor.
… share (TH-8665) export() wrote the mapped attributes back onto the ReadableSpan it was handed. Every processor on the provider holds that same object, LiveKit Cloud's exporter among them, and LiveKit redacts by attribute name. With lk.pii.* now mapped, the conversation landed under input.value, output.value, metadata, gen_ai.conversation.id and user.id, which its redaction does not strip: on 1.8 for spans held until a session turns redaction on, on 1.7 for every span. The exporter now builds a new ReadableSpan with the mapped attributes, as LiveKit's own pii.redact() does, and sends that; on any failure it sends the original span unmapped. Empty user input, reply, text to speak and tool arguments or output no longer become the JSON text "". Tests: LiveKit's real gated Cloud exporter on the same provider, with job_registered(redacted=True), receives none of our copies; a second exporter on a plain provider sees the span untouched (covers 1.7); the copy keeps every other span field; every renamed attribute pair is pinned; the failure warning is checked against a value leak. Signed-off-by: khushalsonawat <[email protected]>
khushalsonawat
added a commit
that referenced
this pull request
Oct 19, 2026
…and run core tests on core changes (TH-8673) 21 package locks still pinned fi-instrumentation-otel 0.1.11 or 0.1.14, while their code and core tests use attribute names that exist from 1.0; with the core tests now importing each package's own fi-instrumentation-otel, they could not even load. Their locks now resolve 1.1.0, the version a fresh install gets (livekit's lock is left to #273). The locks shrink because 1.1.0 made its evaluation libraries optional. Workflow: pytest, pytest-asyncio and pytest-mock are added only where a package's environment lacks them; a change to fi_instrumentation (or python/tests/pytest.ini) runs every package with core tests; a redundant line in the verdict step is gone. Signed-off-by: khushalsonawat <[email protected]>
Signed-off-by: khushalsonawat <[email protected]>
atharva-bhange
approved these changes
Oct 19, 2026
This was referenced Oct 19, 2026
11 of 12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
It gets LiveKit voice agents sending real traces again, as traceAI-livekit 0.1.2. Not published; publishing waits for the owner.
lk.pii.*, and the exporter only knew the old names. So spans arrived with noinput.valueoroutput.value.lk.pii.*content now mapped, LiveKit Cloud's exporter on the same provider would have received it under names its redaction doesn't strip. The mapping now goes on a copy of each span.Why?
Linear: TH-8665.
1. The exporter.
register()marks its own exporter as a default that the nextadd_span_processorcall replaces (fi_instrumentation/otel.py:243-244).set_tracer_provideradds its privacy filter throughadd_span_processor.enable_*_attribute_mapping()now clears that mark before handing the provider to LiveKit.fi_instrumentationand every other framework are unchanged.2. The content.
lk.user_inputbecamelk.pii.user_input;chat_ctx,response.text,response.function_calls,function_tool.arguments,function_tool.output,input_text,user_transcript,instructions,room_nameandparticipant_identity."[]"as the function calls on every LLM node. An empty list no longer counts as output, sooutput.valueis the reply itself. Before, it was{"text": ..., "function_calls": "[]"}, for the older names too.4. The shared span.
piisegment.input.value,output.value,metadata,gen_ai.conversation.idanduser.idonto that object.input.value"my card is 4111".ReadableSpanwith the mapped attributes, the way LiveKit's ownpii.redact()does, and sends that. If anything fails, it sends the original span unmapped, never dropped."".3. The floor.
GEN_AI_SPAN_KINDandGEN_AI_REQUEST_MODEL, so the mapping raisedAttributeError, which was swallowed.register()with OpenTelemetry 1.45, which livekit-agents 1.8.7 and later require. 1.1.0 works.>=0.1.14to>=1.1.0.register()only gained one optional argument, so code that just callsregister()keeps working. 1.0 moved span attributes fromllm.*to the OpenTelemetry GenAIgen_ai.*names. Code that uses those names directly should follow the existing mapping table, which the README links:docs/OTEL_GENAI_SEMANTIC_CONVENTIONS.md, "Migration from OpenInference".How was it tested?
poetry install, Python 3.10, livekit-agents 1.8.6, fi-instrumentation-otel 1.1.0 from PyPI). They also pass with livekit-agents 1.8.8 on Python 3.13. On livekit-agents 1.7.0, 49 pass; the 1.8-only integration file skips.job_registered(..., redacted=True)is called. Cloud receives none ofinput.value,output.value,gen_ai.conversation.id,user.id, and no secret value anywhere. Meanwhile our exporter does get them.tests/test_mapping.py: the mapping on both attribute-name sets (LLM, agent turn, tool, TTS, session, metadata); the newer name wins; empty function calls; a mapping failure is logged once with no attribute values and the span is still exported, on both code paths.tests/test_integration.py: the exporter is kept (HTTP and gRPC); spans reach it; a later processor runs next to it;register()on its own is unchanged. There is also an end-to-end turn: a real LiveKitAgentSessionturn with a fake model, exported by the real exporter to a local collector, checking the content on thellm_nodeandagent_turnspans. And theLIVEKIT_TELEMETRY_ALLOW_PII=falseopt-out, where content stays out of the export. Each test puts LiveKit's module tracer back afterwards.allow_piiforced on;register()no longer setting the mark.input.valueoroutput.valueon either span.ruff checkpasses on the changed files.set_tracer_providernote.Checklist
maindocs/VOCABULARY.mdis not in this repo)Notes for reviewers
poetry.lockwas re-locked with Poetry 2.1.2. The lock's numpy 2.0.2 has no Python 3.13 wheel, so the dev setup needs Python 3.10 to 3.12.enable_*runs next to the exporter instead of replacing it.lk.pii.instructions, keeps the name it arrived with.redact()does the same, and those counts are diagnostic only.enable_*loses headers passed toregister(headers=...). This predates the PR; customers usingFI_API_KEY/FI_SECRET_KEYare unaffected. Proposed as a follow-up.register()trap is tracked separately as TH-8672.🤖 Generated with Claude Code