LiveKit Agents
Capture LiveKit Agents sessions in Voxmith with the VoxEye SDK, or export native traces over OpenTelemetry
Choose a setup method
| Method | Use it when |
|---|---|
| VoxEye SDK (recommended) | You run LiveKit Agents in Python and want the fastest, fully supported connection: one install and three lines of code. |
| Voxmith skill | You want your coding agent to inspect the app, make the change, and verify it. |
| Voxmith SDK | You want explicit control over the interaction boundaries and fields. |
| OpenTelemetry | The framework already emits useful spans or you operate an OTel pipeline. |
Start with one method for a call path. Combining SDK tracking with framework OpenTelemetry on the same call can create duplicate interactions.
Recommended: VoxEye SDK
Recommended. VoxEye is Voxmith's own SDK for LiveKit Agents. It installs with
one pip command, attaches to your AgentSession in three lines, and sends every
transcript, tool call, span, and per-component model identifier to Voxmith. No event
listeners and no content buffering.
Install
pip install voxeyeVoxEye requires livekit-agents >= 1.0.
Connect your agent
Create the Observability object once at module level, then call attach() inside your entrypoint.
from voxeye import Observability
from livekit.agents import AgentSession, JobContext
obs = Observability(api_key="sk_...", endpoint="https://api.voxmith.com") # module level
async def entrypoint(ctx: JobContext):
session = AgentSession(stt=..., llm=..., tts=...)
obs.attach(session, ctx) # sync, fail-open
await session.start(agent=MyAgent(), room=ctx.room) # your existing lineThat is the whole integration. attach() is wrapped to fail open, so it never raises into your entrypoint.
What attach() does
- Resets any previous
TracerProvider, because LiveKit worker subprocesses are pooled and reused between jobs. - Uses the LiveKit job ID as the
call_id. - Resolves
agent_idfrom youragent_id=override, thenctx.job.agent_name, and falls back to"unknown". Voxmith reconciles the real name from theagent_sessionspan. - Reads
session.stt,.llm,.tts, and.vadand records each component's provider and model on every span. - Installs a gzip OTLP exporter with bearer authentication and an async flush on shutdown.
- Sends a lifecycle ping so a call shows up immediately and crashes are detected.
Notes
- Call
attach()once per entrypoint. Calling it twice replaces the tracer provider mid-call. endpointdefaults tohttp://localhost:8000. Pass your Voxmith endpoint in production.- Do not combine VoxEye with the manual OpenTelemetry setup below on the same call path. VoxEye already exports the native LiveKit spans, and running both can create duplicate interactions.
Redaction controls for LiveKit Agents 1.7 and later are in the making.
Alternative: Voxmith skill
Choose this path when you want your coding agent to inspect the project, select a supported transport, make the smallest instrumentation change, send a test event, and verify that it reached Voxmith.
Install the skill once in your coding-agent environment:
npx skills add Voxmith/skills --skill voxmithRun the framework-specific prompt below from the application root. Review the resulting diff before deploying it.
Use the voxmith skill to add Voxmith analytics to this LiveKit Agents application.
Org ID: your-org-id
Use LiveKit's native OpenTelemetry traces and verify one fresh agent session.Manual setup: OpenTelemetry
LiveKit Agents already instruments agent sessions, turns, model requests, tool calls, speech, and transcription with OpenTelemetry. Point those native spans at Voxmith; no webhook or Voxmith SDK wrapper is required.
1. Configure the OTLP destination
Set these variables in the agent worker environment:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://otel.voxmith.com/v1/traces
OTEL_EXPORTER_OTLP_HEADERS="X-Voxmith-Org-ID=your-org-id"
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf2. Register one batch exporter
Call LiveKit's tracer-provider hook before AgentSession.start().
from livekit.agents.telemetry import set_tracer_provider
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
set_tracer_provider(provider, metadata={
"session.id": conversation_id,
"user.id": user_id,
})import { telemetry } from '@livekit/agents';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
const fanout = new telemetry.FanoutSpanProcessor();
const provider = new NodeTracerProvider({
spanProcessors: [
new BatchSpanProcessor(new OTLPTraceExporter()),
fanout,
],
});
provider.register();
telemetry.setTracerProvider(provider, {
metadata: {
'session.id': conversationId,
'user.id': userId,
},
registerSpanProcessor: (processor) => fanout.add(processor),
});Install the OTel packages if the application does not already have them:
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-httpnpm install @opentelemetry/api @opentelemetry/exporter-trace-otlp-http @opentelemetry/sdk-trace-nodeThe TypeScript FanoutSpanProcessor is required by the current OTel 2.x API so
LiveKit can attach its metadata processor (and its Cloud exporter, when enabled)
after provider construction.
3. Add standard conversation context
Pass metadata to set_tracer_provider; LiveKit copies it to every native span.
Use standard attributes rather than Voxmith-specific names:
set_tracer_provider(provider, metadata={
"session.id": ctx.room.name,
"user.id": customer_id,
"user.plan": "enterprise",
"user.locale": "en-US",
"conversation.channel": "voice",
"conversation.environment": "production",
})| Attribute | Voxmith behavior |
|---|---|
session.id |
Groups every span into one conversation. |
user.id |
Groups conversations into one user. |
user.* or enduser.* |
Promoted to user metadata. |
conversation.* or session.* |
Promoted to conversation metadata. |
Any other attribute, including lk.* |
Preserved on the event. |
If session.id is absent, Voxmith falls back to LiveKit's lk.job_id, then
lk.room_name. For user identity, explicit user.id is best;
the participant identity on a native user-turn span is a safe fallback.
What appears in Voxmith
agent_turnspans become the ordered user/assistant conversation turns.function_toolspans become tool calls with native arguments, output, and error state.- LLM request spans retain model, token, and
gen_ai.*attributes. - STT, TTS, speaking, evaluator, session, job, and activity spans remain visible in the trace.
- Native
lk.*, standardgen_ai.*, and your custom attributes remain available on events.
LiveKit webhooks are useful for room and participant lifecycle events, but they do not carry the complete agent turn, model, tool, and speech span tree. Use native OTLP export for observability.
Flush on shutdown
Batch export is asynchronous. Register a shutdown callback so the worker sends the final turns before it exits:
async def flush_traces():
provider.force_flush()
ctx.add_shutdown_callback(flush_traces)ctx.addShutdownCallback(async () => {
await provider.shutdown();
});Verify
Run one real LiveKit agent session, then open Users in Voxmith. Confirm:
- The user appears under the value sent in
user.id. - The conversation shows alternating user and assistant turns.
- The trace contains the LiveKit agent, LLM, tool, STT, and TTS spans used by the session.
- Tool details show their native input and output.
Troubleshooting
- Register the provider before
AgentSession.start(); earlier spans cannot be recovered. - Use the traces URL ending in
/v1/traceswith the HTTP/protobuf exporter. - Confirm
X-Voxmith-Org-IDis present inOTEL_EXPORTER_OTLP_HEADERS. - Do not point a generic OTLP exporter at LiveKit's private observability URL.
- Flush the provider when a short-lived worker or test exits.
References
- LiveKit: Export traces
- LiveKit Python OTel example
- LiveKit TypeScript OTel example
- LiveKit TypeScript
setTracerProviderreference
Next steps
- Conversations: review complete voice interactions.
- Events: inspect native LiveKit spans and attributes.
- Tool calls: analyze tool arguments, results, errors, and latency.