Skip to main content

Automatically ingest LiveKit Agents session data into the Tuner observability API

Project description

tuner-livekit-sdk

Automatically ingest LiveKit Agents session data into the Tuner observability API.

Installation of the Library into your Livekit project

pip install tuner-livekit-sdk

Quickstart

Set credentials via environment variables:

export TUNER_API_KEY="tr_api_..."
export TUNER_WORKSPACE_ID="123"
export TUNER_AGENT_ID="my-agent"

Then drop the plugin in right after creating your AgentSession:

from tuner import TunerPlugin

async def entrypoint(ctx: JobContext):
    session = AgentSession(...)
    TunerPlugin(session, ctx)   # wires itself automatically
    await session.start(...)

That's it. The plugin listens to session events and submits call data to Tuner when the session ends.

Configuration

Environment variables

Variable Required Description
TUNER_API_KEY Bearer token (starts with tr_api_)
TUNER_WORKSPACE_ID Integer workspace ID
TUNER_AGENT_ID Agent identifier from Tuner Agent Settings
TUNER_BASE_URL API base URL (default: https://api.usetuner.ai)

Credentials from code

Pass credentials directly instead of (or to override) environment variables:

TunerPlugin(
    session, ctx,
    api_key="tr_api_...",
    workspace_id=123,
    agent_id="my-agent",
)

Options

Call type

By default the plugin auto-detects the call type (phone_call for SIP participants, web_call otherwise). Override it explicitly:

TunerPlugin(session, ctx, call_type="phone_call")
TunerPlugin(session, ctx, call_type="web_call")

Recording URL

Tuner requires a recording_url for every call. If you don't provide a resolver the plugin logs a warning and submits "pending" as a placeholder:

# Static URL
async def my_resolver(room_name: str, job_id: str) -> str:
    return f"https://cdn.example.com/recordings/{job_id}.ogg"

TunerPlugin(session, ctx, recording_url_resolver=my_resolver)
# LiveKit Egress → S3
async def egress_resolver(room_name: str, job_id: str) -> str:
    url = await my_egress_db.get_recording_url(room_name)
    return url or "pending"

TunerPlugin(session, ctx, recording_url_resolver=egress_resolver)

Cost calculation

Provide a callable that receives a UsageSummary and returns the call cost in USD cents:

def calculate_cost(usage) -> float:
    llm_cost  = usage.llm_prompt_tokens     * 0.000_003
    llm_cost += usage.llm_completion_tokens * 0.000_015
    tts_cost  = usage.tts_characters_count  * 0.000_030
    stt_cost  = usage.stt_audio_duration    * 0.000_006
    return llm_cost + tts_cost + stt_cost

TunerPlugin(session, ctx, cost_calculator=calculate_cost)

Extra metadata

Attach arbitrary key-value data to every call record:

TunerPlugin(
    session, ctx,
    extra_metadata={
        "env": "production",
        "region": "us-east-1",
        "deployment": "v2.3.1",
    },
)

Retry and timeout

TunerPlugin(
    session, ctx,
    timeout_seconds=15.0,   # per-request timeout (default: 30.0)
    max_retries=5,          # retries on 5xx / 429 / network errors (default: 3)
)

Disable the plugin

Useful for local development or test environments:

import os

TunerPlugin(
    session, ctx,
    enabled=os.getenv("ENV") == "production",
)

Full example

import os
from livekit.agents import JobContext, AgentSession
from tuner import TunerPlugin

def calculate_cost(usage) -> float:
    return (
        usage.llm_prompt_tokens     * 0.000_003
        + usage.llm_completion_tokens * 0.000_015
        + usage.tts_characters_count  * 0.000_030
    )


async def get_recording_url(room_name: str, job_id: str) -> str:
    return await my_storage.get_url(job_id) or "pending"


async def entrypoint(ctx: JobContext):
    session = AgentSession(...)

    TunerPlugin(
        session, ctx,
        api_key=os.environ["TUNER_API_KEY"],
        workspace_id=int(os.environ["TUNER_WORKSPACE_ID"]),
        agent_id="customer-support-v3",
        call_type="phone_call",
        recording_url_resolver=get_recording_url,
        cost_calculator=calculate_cost,
        extra_metadata={"env": "prod", "region": "us-east-1"},
        timeout_seconds=20.0,
        max_retries=3,
        enabled=True,
    )

    await session.start(...)

Requirements

  • Python ≥ 3.10
  • livekit-agents >= 1.4
  • aiohttp >= 3.9

License

MIT

Installation dependencies to build the library

uv sync --dev source .venv/bin/activate

Publish to Pypi

pip install build twine python -m build twine upload dist/*

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tuner_livekit_sdk-0.1.3.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tuner_livekit_sdk-0.1.3-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

Details for the file tuner_livekit_sdk-0.1.3.tar.gz.

File metadata

  • Download URL: tuner_livekit_sdk-0.1.3.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for tuner_livekit_sdk-0.1.3.tar.gz
Algorithm Hash digest
SHA256 6b3925950a436e1516a341556b84969a088dc415e1ecb5afee3602e131cebbf6
MD5 1519e15f4708693c7046cafc3c7b6885
BLAKE2b-256 a617b80d631b4a0b34e658baf152e13cc7ea9d336099b46bb2b7b89c28dd867d

See more details on using hashes here.

File details

Details for the file tuner_livekit_sdk-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for tuner_livekit_sdk-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 5a735f56bf196a86d18822964e8e66610176336c4e899053452a2a3cfcb572a9
MD5 eff653c48c5c9023b334bab7ef8a08d1
BLAKE2b-256 b643a4165b12f5ac0cac6299a23a660e43b2b6a9152830e29ca8531e1eb5b5a4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page