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
15 changes: 9 additions & 6 deletions .agents/skills/openrtc-python/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,21 @@ with `_`. Fix and re-run `openrtc list` until all agents appear.

```bash
# Development mode (auto-reload) — set LIVEKIT_* env vars first
openrtc dev --agents-dir ./agents
openrtc dev ./agents

# Production mode
openrtc start --agents-dir ./agents
openrtc start ./agents

# Same LiveKit subcommands as python agent.py: console, connect, download-files
# openrtc console --agents-dir ./agents
# openrtc connect --agents-dir ./agents --room my-room
# openrtc console ./agents
# openrtc connect ./agents --room my-room
# openrtc list ./agents
# openrtc download-files ./agents

# Optional: JSON Lines metrics + sidecar TUI (pip install 'openrtc[cli,tui]')
# openrtc dev --agents-dir ./agents --metrics-jsonl ./metrics.jsonl
# openrtc tui --watch ./metrics.jsonl
# openrtc dev ./agents ./openrtc-metrics.jsonl
# openrtc tui
# openrtc tui ./other-metrics.jsonl

# Or run the entrypoint directly
python main.py dev
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,13 +409,13 @@ All commands are documented in `CONTRIBUTING.md`. Quick reference:
- **Lint:** `uv run ruff check .`
- **Format check:** `uv run ruff format --check .`
- **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"`
- **CLI demo:** `uv run openrtc list ./examples/agents --default-stt "deepgram/nova-3:multi" --default-llm "openai/gpt-4.1-mini" --default-tts "cartesia/sonic-3"` (same as `--agents-dir ./examples/agents`)

### Non-obvious notes

- 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` 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.
- 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`, requires `openrtc[tui]` / dev deps) tails `./openrtc-metrics.jsonl` by default (same path as `--metrics-jsonl` on the worker; override with `--watch`).
- `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.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,13 @@ If you pass strings such as `openai/gpt-4.1-mini`, OpenRTC leaves them as-is and

## CLI and TUI

Install `openrtc[cli]` to get `openrtc` on your PATH. Subcommands follow the LiveKit Agents CLI shape (`dev`, `start`, `console`, `connect`, `download-files`), plus `list` and `tui`.
Install `openrtc[cli]` to get `openrtc` on your PATH. Subcommands follow the LiveKit Agents CLI shape (`dev`, `start`, `console`, `connect`, `download-files`), plus `list` and `tui`. For most commands you can pass the agents directory (or, for `tui`, the metrics JSONL file) as the first path argument instead of `--agents-dir` / `--watch`.

**List what discovery would register** (defaults are string passthroughs for `livekit-agents`, not constructed provider objects):

```bash
openrtc list \
--agents-dir ./agents \
./agents \
--default-stt openai/gpt-4o-mini-transcribe \
--default-llm openai/gpt-4.1-mini \
--default-tts openai/gpt-4o-mini-tts
Expand All @@ -241,16 +241,18 @@ openrtc list \
**Run a production worker** (after exporting `LIVEKIT_*`):

```bash
openrtc start --agents-dir ./agents
openrtc start ./agents
```

**Run a development worker**:

```bash
openrtc dev --agents-dir ./agents
openrtc dev ./agents
```

Optional visibility: `--dashboard` prints a Rich summary in the terminal. `--metrics-json-file ./runtime.json` overwrites a JSON snapshot on each tick. Use that for scripts, dashboards, or CI. For JSON Lines plus a separate terminal UI, use `--metrics-jsonl ./metrics.jsonl` with `openrtc tui --watch ./metrics.jsonl` after `pip install 'openrtc[cli,tui]'`.
Same as ``openrtc dev --agents-dir ./agents``. The metrics JSONL file is **optional**: add a second path only when you want JSONL output (same as ``--metrics-jsonl``), e.g. ``openrtc dev ./agents ./openrtc-metrics.jsonl`` for ``openrtc tui``.

Optional visibility: `--dashboard` prints a Rich summary in the terminal. `--metrics-json-file ./runtime.json` overwrites a JSON snapshot on each tick. Use that for scripts, dashboards, or CI. For JSON Lines plus a separate terminal UI, use `--metrics-jsonl ./openrtc-metrics.jsonl` on the worker and `openrtc tui` in another terminal (it tails `./openrtc-metrics.jsonl` by default; override with `--watch`) after `pip install 'openrtc[cli,tui]'`.

Stable machine output: `openrtc list --json` and `--plain`. Combine `--resources` when you want footprint hints. OpenRTC-only flags are stripped before the handoff to LiveKit’s CLI parser.

Expand Down
55 changes: 39 additions & 16 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,18 @@ with `1`.
export LIVEKIT_API_SECRET=secret
```

2. Run a worker subcommand with **only** `--agents-dir` (plus any provider
defaults your agents need):
2. Run a worker subcommand with an agents directory (plus any provider defaults
your agents need). You can pass **`--agents-dir`** or use the **first
positional argument** on ``start`` / ``dev`` / ``console``. A **second**
positional is **optional** and only sets ``--metrics-jsonl`` when you want JSONL
metrics (e.g. for ``openrtc tui``); skip it if you only need the agents
directory (unless you already passed ``--metrics-jsonl``).

```bash
openrtc dev --agents-dir ./agents
# or
openrtc dev ./agents
openrtc dev ./agents ./openrtc-metrics.jsonl
# equivalent to:
openrtc dev --agents-dir ./agents --metrics-jsonl ./openrtc-metrics.jsonl
openrtc start --agents-dir ./agents
```

Expand Down Expand Up @@ -74,6 +80,12 @@ flag.

## Commands

Across **list**, **connect**, **download-files**, **start** / **dev** / **console**,
and **tui**, you can often pass paths **positionally** instead of `--agents-dir`,
`--metrics-jsonl`, or `--watch` (see each command below). The first non-flag
token after the subcommand is rewritten before parsing; use `--agents-dir` /
`--watch` when you need a different argument order.

### `openrtc list`

Discovers agent modules and prints each agent’s resolved settings.
Expand All @@ -89,33 +101,33 @@ Discovers agent modules and prints each agent’s resolved settings.
`--help`).

```bash
openrtc list --agents-dir ./agents
openrtc list ./agents
openrtc list --agents-dir ./agents --plain
openrtc list --agents-dir ./agents --json
openrtc list ./agents --json
```

### `openrtc start`

Production-style worker (same role as `python agent.py start`).

```bash
openrtc start --agents-dir ./agents
openrtc start ./agents
```

### `openrtc dev`

Development worker with reload (same role as `python agent.py dev`).

```bash
openrtc dev --agents-dir ./agents
openrtc dev ./agents
```

### `openrtc console`

Local console session (same role as `python agent.py console`).

```bash
openrtc console --agents-dir ./agents
openrtc console ./agents
```

### `openrtc connect`
Expand All @@ -124,7 +136,7 @@ Connect the worker to an existing room (LiveKit `connect`). Requires
`--room`.

```bash
openrtc connect --agents-dir ./agents --room my-room
openrtc connect ./agents --room my-room
```

### `openrtc download-files`
Expand All @@ -134,20 +146,30 @@ directory (for a valid worker entrypoint) plus connection settings—**no**
`--default-stt` / `--default-llm` / `--default-tts` / `--default-greeting`.

```bash
openrtc download-files --agents-dir ./agents
openrtc download-files ./agents
```

### `openrtc tui`

Sidecar Textual UI that tails a **JSON Lines** metrics file written by the
worker (`--metrics-jsonl`). Requires `openrtc[tui]`.

With no flags, the TUI tails **`./openrtc-metrics.jsonl`** in the current working
directory. Pass **`--watch PATH`** or a **positional path** to use another file
(it must match `--metrics-jsonl` on the worker).

```bash
# Terminal 1
openrtc dev --agents-dir ./agents --metrics-jsonl ./openrtc-metrics.jsonl
openrtc dev ./agents ./openrtc-metrics.jsonl

# Terminal 2 (same default file as above)
openrtc tui

# Or pass the file positionally:
# openrtc tui ./openrtc-metrics.jsonl

# Terminal 2
openrtc tui --watch ./openrtc-metrics.jsonl
# Equivalent explicit form:
# openrtc tui --watch ./openrtc-metrics.jsonl
```

Use **`--from-start`** (under **Advanced**) to read the file from the beginning
Expand All @@ -164,7 +186,7 @@ instead of tailing from EOF.
(`snapshot` or `event`), `seq`, `wall_time_unix`, `payload`. Snapshots match
`PoolRuntimeSnapshot.to_dict()`; events carry session lifecycle hints
(`session_started`, `session_finished`, `session_failed`). Intended for
`openrtc tui --watch` and other tail consumers.
`openrtc tui` and other tail consumers.
- **`--dashboard-refresh`** — Interval in seconds for dashboard, metrics file,
and JSONL when `--metrics-jsonl-interval` is not set (**Advanced**).
- **`--metrics-jsonl-interval`** — Override JSONL cadence only (**Advanced**).
Expand Down Expand Up @@ -244,7 +266,8 @@ openrtc list --agents-dir ./examples/agents --resources --json
--metrics-jsonl ./openrtc-metrics.jsonl
```

3. Watch the dashboard (or `openrtc tui --watch ./openrtc-metrics.jsonl`) for
3. Watch the dashboard (or run `openrtc tui` in another terminal for the same
default JSONL file) for
worker RSS, active sessions, routing, and errors.

4. Use `runtime.json` or the JSONL stream for automation or scraping.
Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Install the **Typer/Rich CLI** (`openrtc list`, `openrtc start`, `openrtc dev`,
pip install 'openrtc[cli]'
```

Install the optional **Textual sidecar** for `openrtc tui --watch` with:
Install the optional **Textual sidecar** for `openrtc tui` with:

```bash
pip install 'openrtc[cli,tui]'
Expand All @@ -56,7 +56,7 @@ With `LIVEKIT_URL`, `LIVEKIT_API_KEY`, and `LIVEKIT_API_SECRET` set, the minimal
worker invocation is:

```bash
openrtc dev --agents-dir ./agents
openrtc dev ./agents
```

Use `openrtc start` for production-style runs. See [CLI](./cli) for `console`,
Expand Down
56 changes: 47 additions & 9 deletions src/openrtc/cli_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
import logging
import sys
from pathlib import Path
from typing import Annotated

import typer
Expand All @@ -23,6 +24,7 @@
_run_connect_handoff,
_run_pool_with_reporting,
_strip_openrtc_only_flags_for_livekit,
inject_cli_positional_paths,
)
from openrtc.cli_params import SharedLiveKitWorkerOptions, agent_provider_kwargs
from openrtc.cli_reporter import RuntimeReporter
Expand All @@ -48,14 +50,17 @@
TuiFromStartArg,
TuiWatchPathArg,
)
from openrtc.metrics_stream import DEFAULT_METRICS_JSONL_FILENAME
from openrtc.pool import AgentPool

logger = logging.getLogger("openrtc")

_QUICKSTART_EPILOG = (
"[bold]Typical usage[/bold]: set [code]LIVEKIT_URL[/code], [code]LIVEKIT_API_KEY[/code], "
"and [code]LIVEKIT_API_SECRET[/code], then run "
"[code]openrtc dev --agents-dir PATH[/code] (or [code]start[/code] in production). "
"[code]openrtc dev ./agents[/code] (agents dir only) or add a second path for "
"[code]--metrics-jsonl[/code]; or use [code]--agents-dir[/code]. "
"[code]start[/code] for production. "
"Defaults are conservative (e.g. no dashboard, 1s refresh); tuning flags are under "
"the [bold]Advanced[/bold] group in each command's [code]--help[/code]."
)
Expand All @@ -66,7 +71,11 @@
"Run multiple LiveKit voice agents from one shared worker. Commands match "
"LiveKit Agents ([code]dev[/code], [code]start[/code], [code]console[/code], "
"[code]connect[/code], [code]download-files[/code]) plus [code]list[/code] and "
"[code]tui[/code]. Only [code]--agents-dir[/code] is required for worker commands; "
"[code]tui[/code]. Most commands accept the agents directory as the first "
"positional argument instead of [code]--agents-dir[/code]; "
"[code]start[/code]/[code]dev[/code]/[code]console[/code] also accept a "
"second path for [code]--metrics-jsonl[/code], and [code]tui[/code] can "
"take a metrics file path as the first positional instead of [code]--watch[/code]; "
"credentials use [code]LIVEKIT_*[/code] env vars by default (CLI flags optional)."
),
epilog=_QUICKSTART_EPILOG,
Expand Down Expand Up @@ -137,18 +146,28 @@ def list_command(
print_resource_summary_rich(discovered)


_WORKER_POSITIONAL_HELP = (
" Use [code]openrtc {name} ./agents[/code] or [code]--agents-dir ./agents[/code]; "
"add a second path only when you want JSONL metrics "
f"([code]--metrics-jsonl[/code], e.g. [code]./{DEFAULT_METRICS_JSONL_FILENAME}[/code] for "
"[code]openrtc tui[/code])."
)

_STANDARD_LIVEKIT_WORKER_SPECS: tuple[tuple[str, str], ...] = (
(
"start",
"Run the worker (same role as [code]python agent.py start[/code] with LiveKit).",
"Run the worker (same role as [code]python agent.py start[/code] with LiveKit)."
+ _WORKER_POSITIONAL_HELP.format(name="start"),
),
(
"dev",
"Development worker with reload (same role as [code]python agent.py dev[/code]).",
"Development worker with reload (same role as [code]python agent.py dev[/code])."
+ _WORKER_POSITIONAL_HELP.format(name="dev"),
),
(
"console",
"Local console session (same role as [code]python agent.py console[/code]).",
"Local console session (same role as [code]python agent.py console[/code])."
+ _WORKER_POSITIONAL_HELP.format(name="console"),
),
)

Expand Down Expand Up @@ -273,10 +292,14 @@ def download_files_command(

@app.command("tui")
def tui_command(
watch: TuiWatchPathArg,
watch: TuiWatchPathArg = Path(DEFAULT_METRICS_JSONL_FILENAME),
from_start: TuiFromStartArg = False,
) -> None:
"""Sidecar Textual UI for a --metrics-jsonl stream (requires the ``tui`` extra)."""
"""Sidecar Textual UI tailing JSONL metrics (requires the ``tui`` extra).

With no ``--watch``, tails ``./openrtc-metrics.jsonl`` in the current directory;
start the worker with ``--metrics-jsonl`` set to that same path.
"""
try:
from openrtc.tui_app import run_metrics_tui
except ImportError as exc:
Expand All @@ -285,7 +308,11 @@ def tui_command(
"(the cli extra is required for the openrtc command)."
)
raise typer.Exit(code=1) from exc
run_metrics_tui(watch, from_start=from_start)
try:
run_metrics_tui(watch, from_start=from_start)
except ValueError as exc:
logger.error("%s", exc)
raise typer.Exit(code=1) from None


def main(argv: list[str] | None = None) -> int:
Expand All @@ -307,8 +334,19 @@ def main(argv: list[str] | None = None) -> int:
previous_argv = sys.argv
try:
if argv is not None:
cli.main(args=list(argv), prog_name="openrtc", standalone_mode=True)
injected_args = inject_cli_positional_paths(list(argv))
# Mirror a real CLI invocation so LiveKit handoff logic that
# inspects sys.argv sees the injected arguments (e.g. --reload).
sys.argv = [previous_argv[0], *injected_args]
cli.main(
args=injected_args,
prog_name="openrtc",
standalone_mode=True,
)
else:
if len(sys.argv) >= 2:
tail = inject_cli_positional_paths(list(sys.argv[1:]))
sys.argv = [sys.argv[0], *tail]
cli.main(args=None, prog_name="openrtc", standalone_mode=True)
except SystemExit as exc:
code = exc.code
Expand Down
Loading
Loading