Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/openrtc-python/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: >-
providers, set up agent routing, or run multiple LiveKit agents together with
OpenRTC.
license: MIT
compatibility: Requires Python 3.10+ and uv (or pip). Requires the openrtc package.
compatibility: Requires Python 3.11+ and uv (or pip). Requires the openrtc package.
metadata:
author: mahimailabs
version: "1.0"
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ jobs:
with:
python-version: "3.11"

- name: Install Ruff
- name: Install lint dependencies
run: |
python -m pip install --upgrade pip
python -m pip install ruff
python -m pip install ruff "mypy>=1.19.1"
python -m pip install -e ".[cli,tui]"

- name: Check formatting
run: ruff format --check .

- name: Run lint checks
run: ruff check .

- name: Run mypy
run: mypy src/
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"

- name: Install build dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13"]

steps:
- name: Check out repository
Expand Down
10 changes: 6 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,24 +396,26 @@ When in doubt:

### Services overview

**OpenRTC** is a single Python package (`src/openrtc/`) with no runtime services required for development. All tests use stubs/mocks for LiveKit (see `tests/conftest.py`), so no LiveKit server, API keys, or external providers are needed to run the test suite.
**OpenRTC** is a single Python package (`src/openrtc/`) with no runtime services required for development. Normal `uv sync` installs the real `livekit-agents` wheel, so tests run against the actual SDK. A fallback shim in `tests/conftest.py` only applies when `livekit.agents` cannot be imported. No LiveKit server, API keys, or external *providers* are needed to run the test suite.

### Common dev commands

**Python:** `requires-python` is **3.11+** (see `pyproject.toml`); 3.10 is not supported.

All commands are documented in `CONTRIBUTING.md`. Quick reference:

- **Install deps:** `uv sync --group dev`
- **Tests:** `uv run pytest` (self-contained; no LiveKit server required)
- **Lint:** `uv run ruff check .`
- **Format check:** `uv run ruff format --check .`
- **Type check:** `uv run mypy src/` (3 pre-existing errors as of this writing)
- **Type check:** `uv run mypy src/` (must pass clean; also runs in `.github/workflows/lint.yml`)
- **CLI demo:** `uv run openrtc list --agents-dir ./examples/agents --default-stt "deepgram/nova-3:multi" --default-llm "openai/gpt-4.1-mini" --default-tts "cartesia/sonic-3"`

### Non-obvious notes

- The `tests/conftest.py` creates a fake `livekit.agents` module when the real one isn't importable. This allows tests to run without the full LiveKit SDK. The real SDK *is* installed by `uv sync`, but if you see import weirdness in tests, this shim is the reason.
- The `tests/conftest.py` shim targets the `livekit-agents` pin in `pyproject.toml` (~1.4.x today) and only implements APIs OpenRTC uses. When upgrading LiveKit or adding new `livekit.agents` usage, extend the shim or confirm tests pass with the real SDK (`uv sync` + `uv run pytest`). If imports behave oddly, check whether the shim path is active vs. the real package.
- Version is derived from git tags via `hatch-vcs`. In a dev checkout the version will be something like `0.0.9.dev0+g<hash>`.
- `mypy` has 3 pre-existing errors in `pool.py` — these are not regressions from your changes.
- `mypy` is enforced in CI alongside Ruff; run `uv run mypy src/` before pushing type-sensitive changes.
- Running `openrtc start` or `openrtc dev` requires a running LiveKit server and provider API keys. For development validation, use `openrtc list` which exercises discovery and routing without network dependencies. The optional sidecar metrics TUI (`openrtc tui --watch`, requires `openrtc[tui]` / dev deps) tails `--metrics-jsonl` from a worker in another terminal.
- `pytest-cov` is in the dev dependency group; CI uses `--cov-fail-under=80`; run
`uv run pytest --cov=openrtc --cov-report=xml --cov-fail-under=80` to match.
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ CLI with `pip install 'openrtc[cli]'` and the sidecar TUI with
If you prefer, you can also install the package and dev dependencies with pip,
but `uv` is the preferred workflow for contributors.

**Python version:** OpenRTC requires **3.11+** (transitive `onnxruntime` from
LiveKit plugins does not support 3.10).

## Common development commands

### Run tests
Expand All @@ -31,6 +34,14 @@ but `uv` is the preferred workflow for contributors.
uv run pytest
```

With `uv sync --group dev`, the real `livekit-agents` package is installed, so
pytest uses the upstream SDK—not the fallback shim in `tests/conftest.py`. That
shim only loads when `livekit.agents` is missing (minimal or broken installs). It
is hand-maintained to match APIs OpenRTC uses; when you upgrade the
`livekit-agents` pin in `pyproject.toml` or add new LiveKit imports in `src/`,
re-run the full suite locally and update `conftest.py` if anything still relies
on the stub.

### Run Ruff lint checks

```bash
Expand All @@ -43,6 +54,17 @@ uv run ruff check
uv run ruff format
```

### Type check (mypy)

CI runs `mypy src/` on pull requests (see `.github/workflows/lint.yml`). Locally:

```bash
uv run mypy src/
```

The wheel and sdist ship `src/openrtc/py.typed` (empty PEP 561 marker) so tools
like mypy and pyright treat `openrtc` as a typed dependency.

## Project architecture

Keep these responsibilities in mind when contributing:
Expand Down
36 changes: 28 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Run N LiveKit voice agents in one worker. Pay the model-load cost once.

<div align="center">
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="Python Version"></a>
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.11+-blue.svg" alt="Python Version"></a>
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
<a href="https://pypi.org/project/openrtc/"><img src="https://img.shields.io/pypi/v/openrtc.svg" alt="PyPI version"></a>
<a href="https://codecov.io/gh/mahimairaja/openrtc-python"><img src="https://codecov.io/gh/mahimairaja/openrtc-python/graph/badge.svg?token=W7VQ5FGSA9" alt="codecov"></a>
Expand Down Expand Up @@ -57,11 +57,22 @@ You already ship three voice agents with `livekit-agents`. Each agent is its own

## Installation

OpenRTC **requires Python 3.11 or newer**. The LiveKit Silero / turn-detector
plugins depend on `onnxruntime`, which does not ship supported wheels for
Python 3.10 in current releases—use 3.11+ to avoid install failures.

```bash
pip install openrtc
```

The base install pulls in `livekit-agents[openai,silero,turn-detector]` so shared prewarm has the plugins it expects.
The base install pulls in `livekit-agents[openai,silero,turn-detector]` so shared prewarm has the plugins it expects. The package ships a **PEP 561** `py.typed` marker for downstream type checkers.

With **uv** (recommended in [CONTRIBUTING.md](CONTRIBUTING.md)):

```bash
uv add openrtc
uv add "openrtc[cli,tui]"
```

```bash
pip install 'openrtc[cli]'
Expand Down Expand Up @@ -253,6 +264,7 @@ Everything openrtc exposes publicly is listed here. Anything else is internal an
- `AgentConfig`
- `AgentDiscoveryConfig`
- `agent_config(...)`
- `ProviderValue` — type alias for STT/LLM/TTS slot values (provider ID strings or LiveKit plugin instances)

On `AgentPool`:

Expand All @@ -271,11 +283,18 @@ On `AgentPool`:
```text
src/openrtc/
├── __init__.py
├── cli.py
├── cli_app.py
├── metrics_stream.py
├── tui_app.py
└── pool.py
├── py.typed
├── cli.py # lazy console entry / missing-extra hints
├── cli_app.py # Typer commands and programmatic main()
├── cli_types.py # shared CLI option aliases
├── cli_dashboard.py # Rich dashboard and list output
├── cli_reporter.py # background metrics reporter thread
├── cli_livekit.py # LiveKit argv/env handoff, pool run
├── cli_params.py # shared worker handoff option bundles
├── metrics_stream.py # JSONL metrics schema
├── provider_types.py # ProviderValue and related typing
├── tui_app.py # optional Textual sidecar
└── pool.py # AgentPool, discovery, routing
```

- `pool.py` — `AgentPool`, discovery, routing
Expand All @@ -285,7 +304,8 @@ src/openrtc/

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md).
See [CONTRIBUTING.md](CONTRIBUTING.md). CI runs **Ruff** and **mypy** on pull
requests alongside the test suite.

## License

Expand Down
6 changes: 0 additions & 6 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
# Codecov checks and PR comments. Patch status is informational so small PRs
# are not blocked twice (pytest remains the hard gate for overall %).

# Optional Textual sidecar (`openrtc[tui]`). Excluded from Codecov totals/patch so
# PR checks are not dominated by UI-only lines; `pytest --cov=openrtc` still
# includes it unless you omit it locally.
ignore:
- "**/openrtc/tui_app\\.py"

coverage:
precision: 2
round: down
Expand Down
29 changes: 22 additions & 7 deletions docs/api/pool.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,34 @@
## Imports

```python
from openrtc import AgentConfig, AgentDiscoveryConfig, AgentPool, agent_config
from dataclasses import field
from typing import Any

from livekit.agents import Agent

from openrtc import (
AgentConfig,
AgentDiscoveryConfig,
AgentPool,
ProviderValue,
agent_config,
)
```

`ProviderValue` is `str | Any`: provider ID strings (for example
`openai/gpt-4.1-mini`) or concrete LiveKit plugin instances (for example
`openai.STT(...)`).

## `AgentConfig`

```python
@dataclass(slots=True)
class AgentConfig:
name: str
agent_cls: type[Agent]
stt: Any = None
llm: Any = None
tts: Any = None
stt: ProviderValue | None = None
llm: ProviderValue | None = None
tts: ProviderValue | None = None
greeting: str | None = None
session_kwargs: dict[str, Any] = field(default_factory=dict)
source_path: Path | None = None
Expand All @@ -35,9 +50,9 @@ in pickle state for worker processes.
@dataclass(slots=True)
class AgentDiscoveryConfig:
name: str | None = None
stt: Any = None
llm: Any = None
tts: Any = None
stt: ProviderValue | None = None
llm: ProviderValue | None = None
tts: ProviderValue | None = None
greeting: str | None = None
```

Expand Down
6 changes: 4 additions & 2 deletions docs/cli.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# CLI

OpenRTC ships a console script named `openrtc` (Typer + Rich) for discovery-based
workflows. The implementation lives in `openrtc.cli` / `openrtc.cli_app`; the
programmatic entry is `typer.main.get_command(app).main(...)` (Click’s
workflows. The Typer application and `main()` live in `openrtc.cli_app` (with
helpers in `cli_livekit`, `cli_dashboard`, `cli_reporter`, `cli_types`, and
`cli_params`). The lazy entrypoint and missing-extra hints are in `openrtc.cli`.
The programmatic entry is `typer.main.get_command(app).main(...)` (Click’s
`Command.main`), not the test-only `CliRunner`.

## Installation
Expand Down
3 changes: 2 additions & 1 deletion docs/concepts/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ OpenRTC keeps the public API intentionally narrow.

- unique `name`
- `agent_cls` subclass
- optional `stt`, `llm`, and `tts` providers
- optional `stt`, `llm`, and `tts` values (`ProviderValue | None`: provider ID
strings or plugin instances)
- optional `greeting` generated after `ctx.connect()`
- optional `session_kwargs` forwarded to `AgentSession`
- optional `source_path` when the module file is known (e.g. after discovery), for
Expand Down
17 changes: 14 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

## Requirements

OpenRTC currently supports Python `>=3.10,<3.14` and depends on
`livekit-agents[openai,silero,turn-detector]~=1.4`.
OpenRTC requires Python **`>=3.11,<3.14`** and depends on
`livekit-agents[openai,silero,turn-detector]~=1.4`. **3.10 is not supported**
(LiveKit’s Silero / turn-detector stack pulls `onnxruntime`, which does not ship
wheels for CPython 3.10 in current releases). See the repository’s
`CONTRIBUTING.md` for `uv` workflows.

## Install

Expand All @@ -12,7 +15,15 @@ pip install openrtc
```

The base package includes the LiveKit Silero and turn-detector plugins used by
OpenRTC's shared prewarm path.
OpenRTC's shared prewarm path. The wheel includes **PEP 561** `py.typed` for type
checkers.

With **uv**:

```bash
uv add openrtc
uv add "openrtc[cli,tui]"
```

Install the **Typer/Rich CLI** (`openrtc list`, `openrtc start`, `openrtc dev`,
`openrtc console`, …) with:
Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@ classifiers = [
"Topic :: Multimedia :: Sound/Audio",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3 :: Only",
]
license = "MIT"
requires-python = ">=3.10,<3.14"
requires-python = ">=3.11,<3.14"
dependencies = [
"livekit-agents[openai,silero,turn-detector]~=1.4",
]
Expand Down Expand Up @@ -83,7 +82,7 @@ ignore = [
"tests/conftest.py" = ["E402"]

[tool.mypy]
python_version = "3.10"
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = true
Expand Down
2 changes: 2 additions & 0 deletions src/openrtc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from importlib.metadata import PackageNotFoundError, version

from .pool import AgentConfig, AgentDiscoveryConfig, AgentPool, agent_config
from .provider_types import ProviderValue

try:
__version__ = version("openrtc")
Expand All @@ -13,6 +14,7 @@
"AgentConfig",
"AgentDiscoveryConfig",
"AgentPool",
"ProviderValue",
"__version__",
"agent_config",
]
Loading
Loading