Metadata-Version: 2.4
Name: voxeye
Version: 0.1.0
Summary: OpenTelemetry observability for LiveKit Agents — ships transcripts, tool calls, prompts, and spans to a self-hosted ingest service in three lines.
Project-URL: Homepage, https://github.com/nilayguptaforwork-ctrl/voxeye
Project-URL: Repository, https://github.com/nilayguptaforwork-ctrl/voxeye
Project-URL: Documentation, https://github.com/nilayguptaforwork-ctrl/voxeye#readme
Project-URL: Issues, https://github.com/nilayguptaforwork-ctrl/voxeye/issues
Project-URL: Changelog, https://github.com/nilayguptaforwork-ctrl/voxeye/blob/main/CHANGELOG.md
Author: Manvendra Singh
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agents,livekit,observability,opentelemetry,voice
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: livekit-agents<2,>=1.5
Requires-Dist: opentelemetry-exporter-otlp-proto-http<1.42,>=1.39
Requires-Dist: opentelemetry-sdk<1.42,>=1.39
Provides-Extra: dev
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.25; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: responses>=0.25; extra == 'dev'
Requires-Dist: ruff>=0.7; extra == 'dev'
Provides-Extra: examples
Requires-Dist: livekit-plugins-cartesia; extra == 'examples'
Requires-Dist: livekit-plugins-deepgram; extra == 'examples'
Requires-Dist: livekit-plugins-google; extra == 'examples'
Requires-Dist: livekit-plugins-silero; extra == 'examples'
Requires-Dist: python-dotenv>=1.0; extra == 'examples'
Requires-Dist: requests>=2.32; extra == 'examples'
Description-Content-Type: text/markdown

# voxeye

OpenTelemetry observability for [LiveKit Agents](https://github.com/livekit/agents). One
`pip install`, three lines of code, and every transcript, tool call, system prompt, span, and
per-component model identifier flows to a self-hosted [retina](../retina) ingest service.

No event listeners, no content buffering, no proprietary SDK lock-in — just OTel done right for
voice agents.

## Install

```bash
pip install voxeye
```

## Use

```python
from voxeye import Observability
from livekit.agents import AgentSession, JobContext

obs = Observability(api_key="sk_...", endpoint="https://your-retina-host")  # module-level, pure

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 line
```

That's it. `attach()`:

1. **Resets any prior `TracerProvider`** — LiveKit worker subprocesses are pooled and reused, so
   a provider from a previous job can leak into the next one.
2. **Resolves `call_id = ctx.job.id`** (`room.sid` isn't available until `ctx.connect()`).
3. **Resolves `agent_id`** via `agent_id=` override → `ctx.job.agent_name` → `"unknown"` (the real
   label is reconciled server-side from the `agent_session` span).
4. **Introspects** `session.stt/.llm/.tts/.vad` for `{component}.provider` / `.model`, set as
   resource attributes on every span — closing gaps LiveKit's plugin instrumentation leaves.
5. **Installs an OTLP exporter** (gzip, bearer auth) and an async shutdown flush.
6. **Fires a lifecycle ping** to `/v1/calls` for instant call existence + crash detection.

Every step is wrapped fail-open: `attach()` never raises into your entrypoint.

## Redaction (opt out of exporting sensitive data)

By default everything is exported. Pass a `Redaction` to stop specific data from ever
leaving the process — it's filtered client-side (sensitive resource attrs aren't set, and
span attributes are dropped at the export boundary; live spans are never mutated):

```python
from voxeye import Observability, Redaction

obs = Observability(
    api_key="sk_...",
    redact=Redaction(
        prompts=True,       # drop the system prompt / instructions
        model_names=True,   # drop stt/llm/tts/vad provider+model (resource AND gen_ai.* attrs)
        transcripts=True,   # drop user transcripts + agent response text
        tool_io=True,       # drop function-tool arguments + outputs
        attributes=("my.custom.attr",),  # escape hatch: extra exact keys to drop
    ),
)
```

Spans, durations, token counts, span tree, and tool *names* still flow — only the fields
you flag are withheld. Each flag defaults to `False` (export).

## Notes

- `endpoint` defaults to `http://localhost:8000` (local retina). Point it at your host in prod.
- Call `attach()` **once** per entrypoint. Calling it twice replaces the provider mid-call.
- Requires `livekit-agents >= 1.0`.

## Develop

```bash
uv sync --extra dev
uv run pytest
uv run ruff check src tests && uv run mypy src
```

## Build & publish

```bash
uv build                       # → dist/voxeye-<version>-py3-none-any.whl + .tar.gz
uvx twine check dist/*         # validate metadata + README rendering
pip install dist/voxeye-*.whl  # try it in a clean env
```

Publishing to PyPI is automated via GitHub Actions **trusted publishing** (OIDC, no API
tokens) in `.github/workflows/publish.yml` — it runs on a published GitHub Release. Before
the first release: reserve the `voxeye` name on PyPI and add this repo as a
[trusted publisher](https://docs.pypi.org/trusted-publishers/). Until then the workflow is
inert. To publish manually instead: `uvx twine upload dist/*`.
