openrtc 0.2.2__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openrtc-0.2.2 → openrtc-0.3.0}/PKG-INFO +92 -1
- {openrtc-0.2.2 → openrtc-0.3.0}/README.md +91 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/changelog.md +36 -5
- openrtc-0.3.0/docs/superpowers/plans/2026-06-08-session-observer-protocol.md +954 -0
- openrtc-0.3.0/docs/superpowers/specs/2026-06-08-session-observer-protocol-design.md +252 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/__init__.py +10 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/core/pool.py +63 -2
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/execution/prewarm.py +18 -0
- openrtc-0.3.0/src/openrtc/observability/__init__.py +13 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/observability/metrics.py +37 -0
- openrtc-0.3.0/src/openrtc/observability/observer.py +224 -0
- openrtc-0.3.0/tests/test_savings_readout.py +76 -0
- openrtc-0.3.0/tests/test_session_observer.py +421 -0
- openrtc-0.2.2/tests/integration/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.coderabbit.yaml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.editorconfig +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.env.example +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/FUNDING.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/dependabot.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/audit.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/bench.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/build.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/canary.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/deploy-docs.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/docs.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/integration.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/lint.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/publish.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.github/workflows/test.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.gitignore +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/.pre-commit-config.yaml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/AGENTS.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/CLAUDE.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/CONTRIBUTING.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/LICENSE +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/Makefile +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/assets/banner.png +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/assets/logo.png +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/codecov.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docker-compose.test.yml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/.vitepress/config.ts +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/.vitepress/theme/custom.css +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/.vitepress/theme/index.ts +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/api/pool.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/audit-2026-05-02.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/benchmarks/density-v0.1.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/cli.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/concepts/architecture.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/deployment/github-pages.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/design/agent-server-integration.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/design/job-executor-protocol.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/design/proc-pool-surface.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/design/v0.1.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/examples.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/getting-started.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/index.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/package-lock.json +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/package.json +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/public/banner.png +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/public/logo.png +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/public/logo.svg +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/docs/release-v0.1.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/agents/dental.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/agents/restaurant.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/density_demo.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/.dockerignore +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/.env.example +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/.gitignore +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/Dockerfile +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/README.md +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/app.css +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/components/agents-ui/agent-audio-visualizer-wave.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/components/agents-ui/agent-chat-transcript.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/components/agents-ui/agent-session-provider.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/components/demo-call-page.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/root.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/routes/api.token.ts +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/routes/dentist.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/routes/home.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/routes/restaurant.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/routes.ts +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/welcome/logo-dark.svg +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/welcome/logo-light.svg +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/app/welcome/welcome.tsx +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/package-lock.json +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/package.json +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/public/favicon.ico +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/react-router.config.ts +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/tsconfig.json +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/frontend/vite.config.ts +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/examples/main.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/pyproject.toml +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/commands.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/dashboard.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/entry.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/livekit.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/params.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/reporter.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/cli/types.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/core/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/core/config.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/core/discovery.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/core/routing.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/core/serialization.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/core/turn_handling.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/execution/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/execution/coroutine.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/execution/coroutine_server.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/execution/file_watcher.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/observability/snapshot.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/observability/stream.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/py.typed +0 -0
- {openrtc-0.2.2/src/openrtc/observability → openrtc-0.3.0/src/openrtc/tui}/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/tui/app.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/src/openrtc/types.py +0 -0
- {openrtc-0.2.2/src/openrtc/tui → openrtc-0.3.0/tests/benchmarks}/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/benchmarks/density.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/benchmarks/throughput.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/conftest.py +0 -0
- {openrtc-0.2.2/tests/benchmarks → openrtc-0.3.0/tests/execution}/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/execution/test_file_watcher.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/execution/test_file_watcher_smoke.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/integration/README.md +0 -0
- {openrtc-0.2.2/tests/execution → openrtc-0.3.0/tests/integration}/__init__.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/integration/conftest.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/integration/test_concurrent_real_calls.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/integration/test_coroutine_realroom.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/integration/test_dev_server_fixture.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_cli.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_cli_optional_extra_integration.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_cli_params.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_config.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_backpressure.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_coverage.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_drain.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_isolation.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_job_context.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_lifecycle.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_server.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_skeleton.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_coroutine_smoke.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_dashboard.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_discovery.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_isolation_process_parity.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_metrics_stream.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_pool.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_resources.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_routing.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_serialization.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_throughput_bench.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_tui_app.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/tests/test_turn_handling.py +0 -0
- {openrtc-0.2.2 → openrtc-0.3.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrtc
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Run multiple LiveKit voice agents in a single shared worker process.
|
|
5
5
|
Project-URL: Homepage, https://github.com/mahimailabs/openrtc
|
|
6
6
|
Project-URL: Repository, https://github.com/mahimailabs/openrtc
|
|
@@ -203,6 +203,76 @@ If a module has no `@agent_config`, the agent name defaults to the filename stem
|
|
|
203
203
|
|
|
204
204
|
Discovered agents work with `livekit dev` and spawn-based workers on macOS. For `add()`, define agent classes at module scope so worker reload can import them.
|
|
205
205
|
|
|
206
|
+
## Migrating from livekit-agents
|
|
207
|
+
|
|
208
|
+
Already running one or more `livekit-agents` workers? Each is its own process that
|
|
209
|
+
loads the same VAD and turn-detector models. Collapse them into one `AgentPool`
|
|
210
|
+
worker without changing your agents.
|
|
211
|
+
|
|
212
|
+
**Before** (one worker per agent, N processes):
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
# restaurant_worker.py (plus a near-identical dental_worker.py, support_worker.py, ...)
|
|
216
|
+
from livekit import agents
|
|
217
|
+
from livekit.agents import Agent, AgentSession
|
|
218
|
+
from livekit.plugins import openai, silero
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class RestaurantAgent(Agent):
|
|
222
|
+
def __init__(self) -> None:
|
|
223
|
+
super().__init__(instructions="You help callers book tables.")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
async def entrypoint(ctx: agents.JobContext) -> None:
|
|
227
|
+
session = AgentSession(
|
|
228
|
+
stt=openai.STT(), llm=openai.LLM(), tts=openai.TTS(), vad=silero.VAD.load()
|
|
229
|
+
)
|
|
230
|
+
await session.start(agent=RestaurantAgent(), room=ctx.room)
|
|
231
|
+
await ctx.connect()
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
if __name__ == "__main__":
|
|
235
|
+
agents.cli.run_app(agents.WorkerOptions(entrypoint_fnc=entrypoint))
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**After** (one worker, N agents, one shared prewarm):
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# worker.py
|
|
242
|
+
from livekit.agents import Agent
|
|
243
|
+
from livekit.plugins import openai
|
|
244
|
+
from openrtc import AgentPool
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class RestaurantAgent(Agent): # unchanged
|
|
248
|
+
def __init__(self) -> None:
|
|
249
|
+
super().__init__(instructions="You help callers book tables.")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class DentalAgent(Agent): # unchanged
|
|
253
|
+
def __init__(self) -> None:
|
|
254
|
+
super().__init__(instructions="You help callers manage appointments.")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
pool = AgentPool(default_stt=openai.STT(), default_llm=openai.LLM(), default_tts=openai.TTS())
|
|
258
|
+
pool.add("restaurant", RestaurantAgent)
|
|
259
|
+
pool.add("dental", DentalAgent)
|
|
260
|
+
pool.run()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Your `Agent` subclasses, tools, and provider objects are unchanged. You delete the
|
|
264
|
+
per-worker boilerplate (`entrypoint`, `AgentSession` wiring, `cli.run_app`) and
|
|
265
|
+
register the agents on one pool; OpenRTC owns prewarm, routing, and per-call
|
|
266
|
+
session construction. On the first run the worker logs the win, for example:
|
|
267
|
+
|
|
268
|
+
```text
|
|
269
|
+
OpenRTC: 2 agents in 1 worker (baseline ~410 MB). 2 separate livekit-agents
|
|
270
|
+
workers would cost ~820 MB; sharing one worker saves ~410 MB of idle baseline
|
|
271
|
+
(assumes equal per-worker baselines).
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
See [Routing](#routing) for how each incoming call resolves to one registered agent.
|
|
275
|
+
|
|
206
276
|
## Memory: before and after
|
|
207
277
|
|
|
208
278
|
Assume an illustrative **~400 MB** idle baseline per worker for the shared stack (VAD, turn detector, and similar). Your measured RSS will differ by provider, model, and OS.
|
|
@@ -373,6 +443,27 @@ Pass instantiated provider objects through to `livekit-agents` unchanged, for ex
|
|
|
373
443
|
|
|
374
444
|
If you pass strings such as `openai/gpt-4.1-mini`, OpenRTC leaves them as-is and the LiveKit runtime interprets them for your deployment.
|
|
375
445
|
|
|
446
|
+
## Session observers
|
|
447
|
+
|
|
448
|
+
Attach external telemetry to every session without subclassing or touching OpenRTC internals. Any object with two async methods is a `SessionObserver` (structural typing, no base class):
|
|
449
|
+
|
|
450
|
+
```python
|
|
451
|
+
from openrtc import AgentPool, SessionInfo, SessionOutcome
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class LoggingObserver:
|
|
455
|
+
async def on_session_start(self, info: SessionInfo, session: object) -> None:
|
|
456
|
+
print(f"live: {info.agent_name} in {info.room_name}")
|
|
457
|
+
|
|
458
|
+
async def on_session_end(self, info: SessionInfo, outcome: SessionOutcome) -> None:
|
|
459
|
+
print(f"done: {info.agent_name} -> {outcome.status.value}")
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
pool = AgentPool(observers=[LoggingObserver()]) # or pool.add_observer(...)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
`on_session_start` receives the live `AgentSession`, which is where you subscribe to its metrics. `on_session_end` receives the terminal outcome (`SUCCESS`, `FAILED`, or `CANCELLED`). Observer calls are isolated: a raising or slow observer is logged and skipped, never crashing the session. In `process` isolation mode an observer must be picklable, so build live resources lazily on the first `on_session_start`.
|
|
466
|
+
|
|
376
467
|
## CLI and TUI
|
|
377
468
|
|
|
378
469
|
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`.
|
|
@@ -171,6 +171,76 @@ If a module has no `@agent_config`, the agent name defaults to the filename stem
|
|
|
171
171
|
|
|
172
172
|
Discovered agents work with `livekit dev` and spawn-based workers on macOS. For `add()`, define agent classes at module scope so worker reload can import them.
|
|
173
173
|
|
|
174
|
+
## Migrating from livekit-agents
|
|
175
|
+
|
|
176
|
+
Already running one or more `livekit-agents` workers? Each is its own process that
|
|
177
|
+
loads the same VAD and turn-detector models. Collapse them into one `AgentPool`
|
|
178
|
+
worker without changing your agents.
|
|
179
|
+
|
|
180
|
+
**Before** (one worker per agent, N processes):
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
# restaurant_worker.py (plus a near-identical dental_worker.py, support_worker.py, ...)
|
|
184
|
+
from livekit import agents
|
|
185
|
+
from livekit.agents import Agent, AgentSession
|
|
186
|
+
from livekit.plugins import openai, silero
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class RestaurantAgent(Agent):
|
|
190
|
+
def __init__(self) -> None:
|
|
191
|
+
super().__init__(instructions="You help callers book tables.")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
async def entrypoint(ctx: agents.JobContext) -> None:
|
|
195
|
+
session = AgentSession(
|
|
196
|
+
stt=openai.STT(), llm=openai.LLM(), tts=openai.TTS(), vad=silero.VAD.load()
|
|
197
|
+
)
|
|
198
|
+
await session.start(agent=RestaurantAgent(), room=ctx.room)
|
|
199
|
+
await ctx.connect()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if __name__ == "__main__":
|
|
203
|
+
agents.cli.run_app(agents.WorkerOptions(entrypoint_fnc=entrypoint))
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**After** (one worker, N agents, one shared prewarm):
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
# worker.py
|
|
210
|
+
from livekit.agents import Agent
|
|
211
|
+
from livekit.plugins import openai
|
|
212
|
+
from openrtc import AgentPool
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class RestaurantAgent(Agent): # unchanged
|
|
216
|
+
def __init__(self) -> None:
|
|
217
|
+
super().__init__(instructions="You help callers book tables.")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class DentalAgent(Agent): # unchanged
|
|
221
|
+
def __init__(self) -> None:
|
|
222
|
+
super().__init__(instructions="You help callers manage appointments.")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
pool = AgentPool(default_stt=openai.STT(), default_llm=openai.LLM(), default_tts=openai.TTS())
|
|
226
|
+
pool.add("restaurant", RestaurantAgent)
|
|
227
|
+
pool.add("dental", DentalAgent)
|
|
228
|
+
pool.run()
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Your `Agent` subclasses, tools, and provider objects are unchanged. You delete the
|
|
232
|
+
per-worker boilerplate (`entrypoint`, `AgentSession` wiring, `cli.run_app`) and
|
|
233
|
+
register the agents on one pool; OpenRTC owns prewarm, routing, and per-call
|
|
234
|
+
session construction. On the first run the worker logs the win, for example:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
OpenRTC: 2 agents in 1 worker (baseline ~410 MB). 2 separate livekit-agents
|
|
238
|
+
workers would cost ~820 MB; sharing one worker saves ~410 MB of idle baseline
|
|
239
|
+
(assumes equal per-worker baselines).
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
See [Routing](#routing) for how each incoming call resolves to one registered agent.
|
|
243
|
+
|
|
174
244
|
## Memory: before and after
|
|
175
245
|
|
|
176
246
|
Assume an illustrative **~400 MB** idle baseline per worker for the shared stack (VAD, turn detector, and similar). Your measured RSS will differ by provider, model, and OS.
|
|
@@ -341,6 +411,27 @@ Pass instantiated provider objects through to `livekit-agents` unchanged, for ex
|
|
|
341
411
|
|
|
342
412
|
If you pass strings such as `openai/gpt-4.1-mini`, OpenRTC leaves them as-is and the LiveKit runtime interprets them for your deployment.
|
|
343
413
|
|
|
414
|
+
## Session observers
|
|
415
|
+
|
|
416
|
+
Attach external telemetry to every session without subclassing or touching OpenRTC internals. Any object with two async methods is a `SessionObserver` (structural typing, no base class):
|
|
417
|
+
|
|
418
|
+
```python
|
|
419
|
+
from openrtc import AgentPool, SessionInfo, SessionOutcome
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class LoggingObserver:
|
|
423
|
+
async def on_session_start(self, info: SessionInfo, session: object) -> None:
|
|
424
|
+
print(f"live: {info.agent_name} in {info.room_name}")
|
|
425
|
+
|
|
426
|
+
async def on_session_end(self, info: SessionInfo, outcome: SessionOutcome) -> None:
|
|
427
|
+
print(f"done: {info.agent_name} -> {outcome.status.value}")
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
pool = AgentPool(observers=[LoggingObserver()]) # or pool.add_observer(...)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
`on_session_start` receives the live `AgentSession`, which is where you subscribe to its metrics. `on_session_end` receives the terminal outcome (`SUCCESS`, `FAILED`, or `CANCELLED`). Observer calls are isolated: a raising or slow observer is logged and skipped, never crashing the session. In `process` isolation mode an observer must be picklable, so build live resources lazily on the first `on_session_start`.
|
|
434
|
+
|
|
344
435
|
## CLI and TUI
|
|
345
436
|
|
|
346
437
|
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`.
|
|
@@ -59,6 +59,14 @@ Changes that have landed on `main` but have not yet been tagged for release.
|
|
|
59
59
|
`livekit-agents` and is allowed to fail.
|
|
60
60
|
- New `docker-compose.test.yml` + `tests/integration/conftest.py`
|
|
61
61
|
fixture harness for local and CI integration runs.
|
|
62
|
+
- Public `SessionObserver` protocol (`openrtc.SessionObserver`,
|
|
63
|
+
`SessionInfo`, `SessionOutcome`, `SessionStatus`) plus
|
|
64
|
+
`AgentPool(observers=[...])` and `AgentPool.add_observer(...)`. External
|
|
65
|
+
telemetry attaches to each live session through the pool:
|
|
66
|
+
`on_session_start` hands the live `AgentSession`, `on_session_end` the
|
|
67
|
+
terminal outcome. Observer faults are isolated (logged and skipped,
|
|
68
|
+
bounded by a timeout) and never crash the session. Additive and backward
|
|
69
|
+
compatible; the built-in metrics store is unchanged.
|
|
62
70
|
|
|
63
71
|
**Changed**
|
|
64
72
|
|
|
@@ -147,12 +155,35 @@ contributor onboarding matches what's in the repo.
|
|
|
147
155
|
|
|
148
156
|
<!-- releases -->
|
|
149
157
|
|
|
158
|
+
## [0.2.3] - 2026-05-30
|
|
159
|
+
|
|
160
|
+
### Added
|
|
161
|
+
- Day-one savings readout: each worker logs the fleet-collapse idle-baseline memory saved (N agents in one shared-prewarm worker vs N separate livekit-agents workers) once at startup, for both `pool.run()` and `openrtc dev/start`, with no `--dashboard` flag. The line claims only idle baseline saved (not per-session density), stays neutral for a single agent, degrades gracefully when RSS is unavailable, and names its equal-baseline assumption.
|
|
162
|
+
- README: a one-screen "Migrating from livekit-agents" recipe (N per-agent workers to one AgentPool).
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## [0.2.2] - 2026-05-30
|
|
167
|
+
|
|
168
|
+
### Fixed
|
|
169
|
+
- Coroutine mode now establishes the LiveKit job context for the session duration, so `get_job_context()` works inside agents and sessions and shutdown callbacks run (MAH-158).
|
|
170
|
+
- Coroutine sessions are held open until the call ends (room disconnect or `ctx.shutdown()`) instead of being marked SUCCESS when the entrypoint returns, so `max_concurrent_sessions` backpressure and runtime session counts are accurate (MAH-160).
|
|
171
|
+
|
|
172
|
+
### Added
|
|
173
|
+
- Real-audio throughput benchmark (`tests/benchmarks/throughput.py`) reporting steady-state event-loop p99 vs session count, separating startup from steady state (MAH-163).
|
|
174
|
+
- `examples/density_demo.py`: a no-server demo comparing process-per-session vs coroutine-pool resident memory.
|
|
175
|
+
|
|
176
|
+
### Changed
|
|
177
|
+
- The coroutine real-room integration test is now a correctness gate (job context plus no-failure); throughput moved to the dedicated benchmark.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
150
181
|
## [0.2.1] - 2026-05-06
|
|
151
182
|
|
|
152
|
-
## What's Changed
|
|
153
|
-
* [v0.2.1] File watcher infrastructure for agent code (MAH-80) by @mahimairaja in https://github.com/mahimailabs/openrtc-runtime/pull/39
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
## What's Changed
|
|
184
|
+
* [v0.2.1] File watcher infrastructure for agent code (MAH-80) by @mahimairaja in https://github.com/mahimailabs/openrtc-runtime/pull/39
|
|
185
|
+
|
|
186
|
+
|
|
156
187
|
**Full Changelog**: https://github.com/mahimailabs/openrtc-runtime/compare/v0.1.0...v0.2.1
|
|
157
188
|
|
|
158
189
|
---
|
|
@@ -162,7 +193,7 @@ contributor onboarding matches what's in the repo.
|
|
|
162
193
|
## What's Changed
|
|
163
194
|
* Feat: light websocket by @mahimairaja in https://github.com/mahimailabs/openrtc-runtime/pull/30
|
|
164
195
|
* docs: bring docs/ in sync with v0.1 surface by @mahimairaja in https://github.com/mahimailabs/openrtc-runtime/pull/35
|
|
165
|
-
* Feat:
|
|
196
|
+
* Feat: structural refactor by @mahimairaja in https://github.com/mahimailabs/openrtc-runtime/pull/36
|
|
166
197
|
* Feat/coroutine pool by @mahimairaja in https://github.com/mahimailabs/openrtc-runtime/pull/37
|
|
167
198
|
* Feat/coroutine pool prod by @mahimairaja in https://github.com/mahimailabs/openrtc-runtime/pull/38
|
|
168
199
|
|