Metadata-Version: 2.4
Name: roark-analytics-python-livekit
Version: 0.1.0
Summary: Roark analytics integration for LiveKit Agents — capture call lifecycle, transcripts, tool calls, metrics, and stereo recordings from any LiveKit Agents session.
Project-URL: Homepage, https://roark.ai
Project-URL: Documentation, https://docs.roark.ai/integrations/livekit
Project-URL: Repository, https://github.com/roarkhq/sdk-roark-analytics-python-livekit
Project-URL: Issues, https://github.com/roarkhq/sdk-roark-analytics-python-livekit/issues
Author-email: Roark <support@roark.ai>
License: MIT License
        
        Copyright (c) 2026 Roark, Inc.
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: agents,analytics,livekit,observability,roark,voice-ai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Requires-Dist: livekit-agents<2,>=1.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: examples
Requires-Dist: python-dotenv>=1.0; extra == 'examples'
Description-Content-Type: text/markdown

# roark-analytics-python-livekit

A [Roark](https://roark.ai) analytics integration for
[LiveKit Agents](https://docs.livekit.io/agents/). Drop one helper into your
agent entrypoint — Roark captures call lifecycle, transcripts, tool calls,
metrics, and a stereo audio recording. No other code changes required.

- **Tested with** `livekit-agents` 1.x
- **Python** 3.10+
- **Built for self-hosted LiveKit** — works against your own `livekit-server`
  and in local `console` mode

> Maintained by [Roark](https://roark.ai). File issues at
> <https://github.com/roarkhq/sdk-roark-analytics-python-livekit/issues>.

---

## Contents

- [Quick start](#quick-start)
- [How it works](#how-it-works)
- [Examples](#examples)
- [Kill switches](#kill-switches)
- [Troubleshooting](#troubleshooting)
- [Configuration reference](#configuration-reference)
- [Development](#development)
- [License](#license)

---

## Quick start

### 1. Install

```bash
pip install roark-analytics-python-livekit
```

### 2. Configure

Set one env var:

```bash
ROARK_API_KEY=rk_live_...
```

> The Roark API key is all you configure — the helpers know their own service
> endpoints.

### 3. Wire `observe_session`

```python
from livekit.agents import Agent, AgentSession, JobContext
from roark_analytics_python_livekit import observe_session

SYSTEM_PROMPT = "You are a friendly voice assistant."

class Assistant(Agent):
    def __init__(self):
        super().__init__(instructions=SYSTEM_PROMPT)

async def entrypoint(ctx: JobContext):
    # Connect before observe_session: the room sid (used as the call id) is only
    # available once connected.
    await ctx.connect()

    session = AgentSession(stt=..., llm=..., tts=...)

    await observe_session(
        ctx, session,
        api_key="rk_live_...",
        agent_id="support-bot-v3",
        agent_name="Support Bot v3",
        agent_prompt=SYSTEM_PROMPT,
    )

    await session.start(room=ctx.room, agent=Assistant())
```

That's it — transcripts, tool calls, metrics, and the stereo recording flow
to Roark automatically.

---

## How it works

The helper subscribes to the standard `AgentSession` event surface and ships
a compact event timeline to Roark:

| Phase | Source | What's captured |
|---|---|---|
| **Session start** | `JobContext.connect()` | `call-started` POST. Agent is lazy-registered on Roark the first time it sees this `agent_id`. |
| **Transcripts** | `session.on("conversation_item_added")` | `ChatMessage` role + content. Both user and assistant turns. |
| **Tool calls** | `session.on("function_tools_executed")` | Paired `tool_call` / `tool_result` records, keyed by `tool_call_id`. |
| **Metrics** | `session.on("metrics_collected")` | EOU / STT / LLM / TTS / Agent latency + LLM token usage. |
| **Audio** | Taps on `session.input.audio` (user) + `session.output.audio` (agent) | Stereo PCM (L=user, R=agent), chunked PUTs to Roark via `/v1/integrations/livekit-sdk/chunk-upload-url`. Tapping the session's own audio I/O works the same in `dev` (room) and `console` mode. |
| **Session end** | `ctx.add_shutdown_callback(...)` (or explicit `await state.aflush()`) | Flushes pending state, drains in-flight uploads, POSTs `call-ended`. Roark stitches the chunks into a WAV on its side. |

Failures are logged and swallowed — **the helpers never raise into the
session**. Your agent keeps running even if Roark is unreachable.

---

## Examples

A complete, runnable self-hosted example ships in
[`examples/`](./examples/) — a [uv](https://docs.astral.sh/uv/) project with a
Roark-instrumented support agent plus a short README. The simplest way to try it
is local console mode (mic + speakers, no LiveKit server or browser needed):

```bash
cd examples
uv sync
cp .env.example .env      # fill in provider keys + ROARK_API_KEY
uv run --env-file .env agent.py console
```

See [`examples/README.md`](./examples/README.md) for local-server mode (a
self-hosted `livekit-server` + a room client). Everything runs on your own
machine — no LiveKit Cloud.

---

## Kill switches

Use these env vars to disable Roark instrumentation at runtime without
touching code:

| Variable | Effect |
|----------|--------|
| `ROARK_OBSERVABILITY_ENABLED=false` | `observe_session` becomes a no-op (returns `None`). |

Treated as off: `false`, `0`, `no`, `off` (case-insensitive). Anything else
(including the variable being absent) keeps the feature enabled.

---

## Troubleshooting

<details>
<summary><strong>Calls aren't finalizing on Roark</strong></summary>

<br>

The helper registers a `shutdown_callback` on the `JobContext`, which fires
when LiveKit ends the job. If your transport tears down without firing the
hook, call `await state.aflush(reason="...")` explicitly from your own
disconnect handler — `aflush()` is idempotent.

</details>

<details>
<summary><strong>Transcripts arrive empty</strong></summary>

<br>

Transcripts are sourced from the `conversation_item_added` event on
`AgentSession`. If you're using a custom pipeline that bypasses
`AgentSession.start(room=…, agent=…)`, that event may never fire — verify by
adding your own `session.on("conversation_item_added", print)` listener.

</details>

<details>
<summary><strong>Recording is missing one side</strong></summary>

<br>

The user side is tapped from `session.input.audio`; the agent side from
`session.output.audio`. The taps are installed by `observe_session`, so it
**must be called before `session.start()`** (otherwise the session captures
its audio I/O before the taps are in place). Check the worker logs for
`tapping user audio input` / `tapping agent audio output` INFO lines — if one
is missing, that side will be silent on the merged stereo recording.

</details>

---

## Configuration reference

| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| `api_key` | `str` | — | **Required.** Roark API key. |
| `agent_id` | `str` | — | **Required.** Customer-stable agent identifier. |
| `agent_name` | `str \| None` | `None` | Display name. |
| `agent_prompt` | `str \| None` | `None` | System prompt; persisted as the agent's prompt revision. |
| `livekit_call_id` | `str \| None` | `ctx.job.room.sid` → `ctx.room.sid` → `ctx.job.id` → UUID | Stable call identifier, sent on every Roark record as `livekitCallId`. Defaults to the room sid (the id Roark keys the call on); resolve the live `ctx.room.sid` by calling `observe_session` after `ctx.connect()`. |
| `capture_audio` | `bool` | `True` | Set to `False` to skip stereo capture (saves bandwidth). |
| `capture_logs` | `bool` | `True` | Reserved for future log streaming. |
| `is_test` | `bool` | `False` | Tag the call as a test on the Roark dashboard. |
| `**metadata` | `dict` | `{}` | Free-form metadata forwarded on `call-started`. |

---

## Development

```bash
pip install -e ".[dev]"
pytest
ruff check .
```

---

## License

MIT — see [LICENSE](./LICENSE).
