reactor-runtime 3.8.2__tar.gz → 3.9.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.
- reactor_runtime-3.9.0/PKG-INFO +214 -0
- reactor_runtime-3.9.0/README.md +185 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/pyproject.toml +23 -5
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/pyproject.toml.orig +28 -10
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/__init__.py +28 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/model.py +6 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/service.py +94 -0
- reactor_runtime-3.9.0/src/reactor_runtime/core/stages.py +71 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/stats.py +4 -1
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/transport.py +182 -5
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/values.py +187 -15
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/server.py +5 -1
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/spec.py +8 -5
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/reactor_app.py +325 -26
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/errors.py +4 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/messages.py +5 -4
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/bridge.py +130 -12
- reactor_runtime-3.9.0/src/reactor_runtime/interface/internal/input_buffer.py +327 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/reactor_core.py +242 -20
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/reactor_pipeline.py +4 -1
- reactor_runtime-3.9.0/src/reactor_runtime/interface/tracks/descriptors.py +117 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/manifest.py +123 -7
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/message_gateway.py +77 -2
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/base.py +20 -1
- reactor_runtime-3.9.0/src/reactor_runtime/protocol/frames.py +308 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/client_stats.py +62 -3
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/connection_manager.py +310 -67
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/offer_epochs.py +5 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/runner.py +970 -80
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/runtime_stats.py +74 -4
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/session_start.py +36 -2
- reactor_runtime-3.9.0/src/reactor_runtime/runner/stages.py +154 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/system_client.py +22 -7
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/schema.py +12 -3
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/serve.py +11 -3
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/__init__.py +2 -2
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/mp4.py +17 -4
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/store.py +7 -2
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/__init__.py +2 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/__init__.py +11 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/connection.py +267 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/media_in.py +488 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/pacer.py +253 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/participant.py +580 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/room.py +556 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/router.py +293 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/signals.py +77 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/version.py +9 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/router.py +331 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/client_stats.py +73 -14
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/connection.py +43 -4
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/frame_stages.py +31 -22
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/frames.py +40 -0
- reactor_runtime-3.9.0/src/reactor_runtime/transport/webrtc/message_stages.py +120 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/pacer.py +92 -28
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/peer.py +352 -34
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/reading.py +24 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/router.py +52 -58
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/signaling.py +16 -1
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/stats.py +63 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/version.py +7 -1
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/data_pb2.py +6 -5
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/data_pb2.pyi +9 -4
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/platform_pb2.py +24 -20
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/platform_pb2.pyi +44 -6
- reactor_runtime-3.9.0/src/reactor_wire/v1/step_pb2.py +51 -0
- reactor_runtime-3.9.0/src/reactor_wire/v1/step_pb2.pyi +103 -0
- reactor_runtime-3.8.2/PKG-INFO +0 -201
- reactor_runtime-3.8.2/README.md +0 -174
- reactor_runtime-3.8.2/src/reactor_runtime/interface/internal/input_buffer.py +0 -195
- reactor_runtime-3.8.2/src/reactor_runtime/interface/tracks/descriptors.py +0 -62
- reactor_runtime-3.8.2/src/reactor_runtime/transport/router.py +0 -159
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/LICENSE +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/NOTICE +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/codes.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/fields.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/naming.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/session.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/typespec.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/errors.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/ipc.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/protocol.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/rank.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/runner.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/worker.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/event_stream.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/events.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/routes.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/step_usage.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/input_state.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/outcome.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/step_completed.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/client.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/decorators.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/aliases.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/contract.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/reactor_model.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/schema.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/idle.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/input_state.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/tracks/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/tracks/input.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/tracks/output.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/log.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/command.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/model.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/registry.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/session.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/paths.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/common.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v0/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v0/codec.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v1/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v1/codec.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/py.typed +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/chunk_encoder.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/markers.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/recorder.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/state_machine.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/upload_resolution.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/service.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/usage.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/acceptor.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/__init__.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/acceptor.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/config.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/metrics.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/sdp.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/upload_store.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/common_pb2.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/common_pb2.pyi +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/control_pb2.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/control_pb2.pyi +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/model_pb2.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/model_pb2.pyi +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/track_pb2.py +0 -0
- {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/track_pb2.pyi +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reactor-runtime
|
|
3
|
+
Version: 3.9.0
|
|
4
|
+
Summary: A Python framework for building real-time, interactive video models
|
|
5
|
+
Author: Reactor
|
|
6
|
+
Author-email: Reactor <team@reactor.inc>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: NOTICE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Dist: av>=18.0.0
|
|
17
|
+
Requires-Dist: fastapi>=0.139.0
|
|
18
|
+
Requires-Dist: numpy>=2.1
|
|
19
|
+
Requires-Dist: prometheus-client>=0.26.0
|
|
20
|
+
Requires-Dist: protobuf>=7.35.1
|
|
21
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
22
|
+
Requires-Dist: reactor-webrtc==0.24.0
|
|
23
|
+
Requires-Dist: uvicorn>=0.49.0
|
|
24
|
+
Requires-Dist: livekit==1.1.21 ; extra == 'livekit'
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Project-URL: Source, https://github.com/reactor-team/reactor-runtime
|
|
27
|
+
Provides-Extra: livekit
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
<div align="center">
|
|
31
|
+
|
|
32
|
+
<img src="assets/banner.png" alt="Reactor Runtime" width="100%" />
|
|
33
|
+
|
|
34
|
+
**Build real-time AI models in Python.**
|
|
35
|
+
|
|
36
|
+
[📖 Documentation](https://docs.reactor.inc/deploy/overview) · [🚀 Quickstart](https://docs.reactor.inc/deploy/development/quickstart) · [🌐 Reactor](https://reactor.inc)
|
|
37
|
+
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
Reactor Runtime turns an inference pipeline into a real-time, interactive media and data stream. You write `load()` and `generate()`; the runtime drives them one step at a time and handles the session lifecycle, the media transport (WebRTC, or a LiveKit room), and the wire protocol that connects clients to your model. Viewers watch frames as they are generated and change what the model is doing mid-stream, with no restart and no re-queue.
|
|
43
|
+
|
|
44
|
+
## Highlights
|
|
45
|
+
|
|
46
|
+
- 📡 **Real-time streaming.** Frames reach clients over WebRTC as your model produces them, not after a whole video is done. The runtime paces playout from the time each step took, so a model needs no rate limiter of its own.
|
|
47
|
+
- 🎮 **Live interaction.** Clients send commands mid-generation: change a prompt, move a camera, adjust a parameter. The next frame reflects it.
|
|
48
|
+
- 🔌 **No transport code.** You never import a WebRTC library, manage a WebSocket, or encode video. The runtime ships its own media engine as a wheel, so a plain Python container is all a model needs.
|
|
49
|
+
- 🏠 **LiveKit rooms.** A session can join a LiveKit room: the model's tracks are published there, and every other participant in the room is a client.
|
|
50
|
+
- ✅ **Typed, validated commands.** Declare the commands your model accepts with standard Python types and constraints. The runtime validates every payload before your handler runs and compiles the surface into an OpenAPI schema that drives typed client SDKs.
|
|
51
|
+
- 🗂️ **Step results.** Start a session with its input and a number of steps, and keep each step's output as a folder of files.
|
|
52
|
+
- 🔎 **Traceable logs.** `get_logger()` writes structured records, and every record a session writes carries its id, so one filter recovers a whole run.
|
|
53
|
+
- 📦 **One container, anywhere.** The `reactor` CLI scaffolds a workspace, builds a small image, and runs it locally. The same image deploys to [Reactor](https://reactor.inc)'s GPU cloud unchanged.
|
|
54
|
+
|
|
55
|
+
## How it works
|
|
56
|
+
|
|
57
|
+
You ship one `ReactorApp` subclass: the application the runtime drives and the client talks to. Declare the media it sends and the state a client can set, load your weights once, and write what one step of generation does:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from pathlib import Path
|
|
61
|
+
|
|
62
|
+
from reactor_runtime import InputField, InputState, Output, ReactorApp, Video
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class MyState(InputState):
|
|
66
|
+
prompt: str = InputField(default="a sunny meadow", moderate=True, description="Scene to render.")
|
|
67
|
+
paused: bool = InputField(default=False, description="Hold generation on the last frame.")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class MyOutput(Output):
|
|
71
|
+
main_video: Video
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class MyModel(ReactorApp):
|
|
75
|
+
state: MyState
|
|
76
|
+
|
|
77
|
+
def load(self, config_path: Path | None) -> None:
|
|
78
|
+
self.pipe = load_my_pipeline()
|
|
79
|
+
|
|
80
|
+
def generate(self, input: MyState) -> MyOutput:
|
|
81
|
+
return MyOutput(main_video=self.pipe.forward(prompt=input.prompt))
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
That is a complete application. The runtime calls `generate()` in a loop for as long as someone is watching and emits what it returns. Every public field on `MyState` is a command the client can send: here `set_prompt` and `set_paused`, validated from the fields, and the next step reads the new values.
|
|
85
|
+
|
|
86
|
+
A step is three calls, and `generate()` is the one you must write. `process_input()` runs before it and decides whether a step can happen: return the input `generate()` gets, or raise `ApplicationError("reason")` to skip the step. `process_output(outcome)` runs after it, with the result or the error, and returns the media to emit; a message sent there with `await self.send()` reaches the client before the step's frames. Both have defaults, so the model above writes neither.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from reactor_runtime import ApplicationError, MessageField, ModelMessage, StepOutcome
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class FrameReady(ModelMessage):
|
|
93
|
+
prompt: str = MessageField(description="The prompt this frame was rendered from.")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class MyModel(ReactorApp):
|
|
97
|
+
state: MyState
|
|
98
|
+
|
|
99
|
+
async def process_input(self) -> MyState:
|
|
100
|
+
if self.state.paused:
|
|
101
|
+
raise ApplicationError("paused")
|
|
102
|
+
return self.state
|
|
103
|
+
|
|
104
|
+
def generate(self, input: MyState) -> MyOutput: ...
|
|
105
|
+
|
|
106
|
+
async def process_output(self, outcome: StepOutcome) -> Output | None:
|
|
107
|
+
if outcome.error is not None:
|
|
108
|
+
raise outcome.error
|
|
109
|
+
await self.send(FrameReady(prompt=self.state.prompt))
|
|
110
|
+
return outcome.to_output()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`generate()` fails by raising, and the exception reaches `process_output()` as `outcome.error`. Recover an error you expect there and return `None` to go on to the next step. Re-raise anything else to end the session with an error, as the default `process_output()` does.
|
|
114
|
+
|
|
115
|
+
Commands and lifecycle hooks such as `@session_started` and `@connected` run between steps, never during one. A command handler refuses by raising `CommandError(code, message)`, which fails the caller's own request. For a loop that is not one step per emit, override `run()` and write it against `emit()`, `send()`, and `complete_step()`.
|
|
116
|
+
|
|
117
|
+
A session can also start with its setup in hand and run a set number of steps: `POST /start_session` takes a `starting_input` of state values and commands, and `steps`, and the session ends once the model has reported that many. Each step can be kept as a folder of files, turned on in `reactor.yaml`:
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
runtime:
|
|
121
|
+
step_results:
|
|
122
|
+
enabled: true
|
|
123
|
+
video: {codec: h264, preset: veryfast, crf: 23} # or codec: h265
|
|
124
|
+
audio: {codec: aac, bitrate_kbps: 128}
|
|
125
|
+
queue: 8
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
A step's tracks are saved as one `output.mp4`, with a `result.json` that lists the step's files, and `GET /sessions/{id}/steps` serves the folders.
|
|
129
|
+
|
|
130
|
+
A session can also be stepped: `POST /start_session` with `"generation": "stepped"` runs one step per `RequestStep` a client sends on the data channel instead of streaming, and answers each request with a `StepResult` that carries the step's media, so a stepped client must negotiate data-channel chunking. The model code is the same in both modes; a model that overrides `run()` or `submit_command()` cannot be stepped. The [stepped echo example](./examples/stepped_echo/README.md) answers each step with what it saw, so a client can check every part of its request.
|
|
131
|
+
|
|
132
|
+
A session can join a LiveKit room. The room connection publishes the model's output tracks, and every other participant in the room is a client of the session. The model's `reactor.yaml` says which RPC methods, data topics, and byte streams reach which of its commands, messages, and input tracks:
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
runtime:
|
|
136
|
+
livekit:
|
|
137
|
+
rpc:
|
|
138
|
+
- method: lk.clear_buffer # a call to this method runs clear_buffer
|
|
139
|
+
command: clear_buffer
|
|
140
|
+
- method: lk.playback_finished # sending playback_finished calls this method
|
|
141
|
+
message: playback_finished
|
|
142
|
+
topics: [avatar] # a data packet on avatar.<command> runs <command>
|
|
143
|
+
media_inputs:
|
|
144
|
+
lk.audio_stream: agent_audio # byte streams on this topic feed agent_audio
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The LiveKit SDK is an optional part of the runtime, so a model that joins rooms lists `reactor-runtime[livekit]` in its `requirements.txt`. A running session joins a room through a connection of its own:
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
POST /sessions/{session_id}/transport/livekit/connections
|
|
151
|
+
{"url": "wss://example.livekit.cloud", "token": "eyJhbGciOi..."}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Scaffold, build, and run it with the CLI:
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
reactor init my-model
|
|
158
|
+
cd my-model
|
|
159
|
+
reactor run
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`reactor run` builds a container with the runtime inside and serves WebRTC signaling on port 8080. Point a browser at it with the [JS SDK](https://docs.reactor.inc/sdk-reference/using-the-sdk), or connect from the [Reactor Sandbox](https://reactor-sandbox.vercel.app/) and watch frames stream immediately.
|
|
163
|
+
|
|
164
|
+
Log from the same import, passing context as keyword arguments:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from reactor_runtime import get_logger
|
|
168
|
+
|
|
169
|
+
logger = get_logger(__name__)
|
|
170
|
+
|
|
171
|
+
logger.info("scene changed", prompt=self.prompt)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Records render as `key=value` text, or as one JSON object per line under `REACTOR_LOG_FORMAT=json`. Every record written during a session carries its id and the runtime's lifecycle state, including the records of `logging.getLogger()` and of the libraries your model imports.
|
|
175
|
+
|
|
176
|
+
## Install
|
|
177
|
+
|
|
178
|
+
Everything runs through the [`reactor` CLI](https://docs.reactor.inc/deploy/platform/installation). There is nothing to install on your host but the CLI and Docker; the runtime ships inside the image the CLI builds for your workspace.
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
brew install reactor-team/tools/reactor-cli
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Not on macOS, or pinning a release in CI? See [Install the CLI](https://docs.reactor.inc/deploy/platform/installation).
|
|
185
|
+
|
|
186
|
+
## Learn more
|
|
187
|
+
|
|
188
|
+
- [Quickstart](https://docs.reactor.inc/deploy/development/quickstart): from zero to a model deployed on Reactor's GPUs
|
|
189
|
+
- [Runtime overview](https://docs.reactor.inc/deploy/development/overview): what the runtime handles, and the outline of a model
|
|
190
|
+
- [Model anatomy](https://docs.reactor.inc/deploy/development/reactor-app/model-anatomy): every member of a `ReactorApp`, line by line
|
|
191
|
+
- [The step loop](https://docs.reactor.inc/deploy/development/reactor-app/step-loop): `process_input()`, `generate()`, `process_output()`, and the frame rate
|
|
192
|
+
- [Events and messages](https://docs.reactor.inc/deploy/development/reactor-app/events-and-messages): commands, refusals with `CommandError`, and messages to clients
|
|
193
|
+
- [Video and audio tracks](https://docs.reactor.inc/deploy/development/reactor-app/video-and-audio-tracks): output and input tracks, and how input is buffered
|
|
194
|
+
- [Starter example](./examples/starter/README.md): the model `reactor init` scaffolds: one class, `generate()` alone, the smallest complete `ReactorApp`
|
|
195
|
+
- [Echo example](./examples/echo/README.md): the client's webcam and microphone in, an effect applied, both sent back, in batches
|
|
196
|
+
- [Waypoint example](./examples/waypoint/README.md): a world model on a GPU, seeded from an upload and steered live
|
|
197
|
+
- [Stepped echo example](./examples/stepped_echo/README.md): one frame and a JSON report back for every step, the reference for a stepped session
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
This repository holds the runtime package itself: the authoring interface, the session runner, the media transport, and the wire protocol. To work on it, use [mise](https://mise.jdx.dev/), which pins the toolchain and forwards every task through a thin `make` shim:
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
mise run install # install deps, generate wire bindings, and git hooks
|
|
205
|
+
mise run lint # ruff check, ruff format --check, and mise.lock drift
|
|
206
|
+
mise run format # apply ruff formatting
|
|
207
|
+
mise run typecheck # ty (strict)
|
|
208
|
+
mise run test # unit tests on the floor Python
|
|
209
|
+
mise run test-matrix # unit tests on every supported Python
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
Licensed under the [Apache License, Version 2.0](./LICENSE).
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/banner.png" alt="Reactor Runtime" width="100%" />
|
|
4
|
+
|
|
5
|
+
**Build real-time AI models in Python.**
|
|
6
|
+
|
|
7
|
+
[📖 Documentation](https://docs.reactor.inc/deploy/overview) · [🚀 Quickstart](https://docs.reactor.inc/deploy/development/quickstart) · [🌐 Reactor](https://reactor.inc)
|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Reactor Runtime turns an inference pipeline into a real-time, interactive media and data stream. You write `load()` and `generate()`; the runtime drives them one step at a time and handles the session lifecycle, the media transport (WebRTC, or a LiveKit room), and the wire protocol that connects clients to your model. Viewers watch frames as they are generated and change what the model is doing mid-stream, with no restart and no re-queue.
|
|
14
|
+
|
|
15
|
+
## Highlights
|
|
16
|
+
|
|
17
|
+
- 📡 **Real-time streaming.** Frames reach clients over WebRTC as your model produces them, not after a whole video is done. The runtime paces playout from the time each step took, so a model needs no rate limiter of its own.
|
|
18
|
+
- 🎮 **Live interaction.** Clients send commands mid-generation: change a prompt, move a camera, adjust a parameter. The next frame reflects it.
|
|
19
|
+
- 🔌 **No transport code.** You never import a WebRTC library, manage a WebSocket, or encode video. The runtime ships its own media engine as a wheel, so a plain Python container is all a model needs.
|
|
20
|
+
- 🏠 **LiveKit rooms.** A session can join a LiveKit room: the model's tracks are published there, and every other participant in the room is a client.
|
|
21
|
+
- ✅ **Typed, validated commands.** Declare the commands your model accepts with standard Python types and constraints. The runtime validates every payload before your handler runs and compiles the surface into an OpenAPI schema that drives typed client SDKs.
|
|
22
|
+
- 🗂️ **Step results.** Start a session with its input and a number of steps, and keep each step's output as a folder of files.
|
|
23
|
+
- 🔎 **Traceable logs.** `get_logger()` writes structured records, and every record a session writes carries its id, so one filter recovers a whole run.
|
|
24
|
+
- 📦 **One container, anywhere.** The `reactor` CLI scaffolds a workspace, builds a small image, and runs it locally. The same image deploys to [Reactor](https://reactor.inc)'s GPU cloud unchanged.
|
|
25
|
+
|
|
26
|
+
## How it works
|
|
27
|
+
|
|
28
|
+
You ship one `ReactorApp` subclass: the application the runtime drives and the client talks to. Declare the media it sends and the state a client can set, load your weights once, and write what one step of generation does:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
from reactor_runtime import InputField, InputState, Output, ReactorApp, Video
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MyState(InputState):
|
|
37
|
+
prompt: str = InputField(default="a sunny meadow", moderate=True, description="Scene to render.")
|
|
38
|
+
paused: bool = InputField(default=False, description="Hold generation on the last frame.")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class MyOutput(Output):
|
|
42
|
+
main_video: Video
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MyModel(ReactorApp):
|
|
46
|
+
state: MyState
|
|
47
|
+
|
|
48
|
+
def load(self, config_path: Path | None) -> None:
|
|
49
|
+
self.pipe = load_my_pipeline()
|
|
50
|
+
|
|
51
|
+
def generate(self, input: MyState) -> MyOutput:
|
|
52
|
+
return MyOutput(main_video=self.pipe.forward(prompt=input.prompt))
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
That is a complete application. The runtime calls `generate()` in a loop for as long as someone is watching and emits what it returns. Every public field on `MyState` is a command the client can send: here `set_prompt` and `set_paused`, validated from the fields, and the next step reads the new values.
|
|
56
|
+
|
|
57
|
+
A step is three calls, and `generate()` is the one you must write. `process_input()` runs before it and decides whether a step can happen: return the input `generate()` gets, or raise `ApplicationError("reason")` to skip the step. `process_output(outcome)` runs after it, with the result or the error, and returns the media to emit; a message sent there with `await self.send()` reaches the client before the step's frames. Both have defaults, so the model above writes neither.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from reactor_runtime import ApplicationError, MessageField, ModelMessage, StepOutcome
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class FrameReady(ModelMessage):
|
|
64
|
+
prompt: str = MessageField(description="The prompt this frame was rendered from.")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class MyModel(ReactorApp):
|
|
68
|
+
state: MyState
|
|
69
|
+
|
|
70
|
+
async def process_input(self) -> MyState:
|
|
71
|
+
if self.state.paused:
|
|
72
|
+
raise ApplicationError("paused")
|
|
73
|
+
return self.state
|
|
74
|
+
|
|
75
|
+
def generate(self, input: MyState) -> MyOutput: ...
|
|
76
|
+
|
|
77
|
+
async def process_output(self, outcome: StepOutcome) -> Output | None:
|
|
78
|
+
if outcome.error is not None:
|
|
79
|
+
raise outcome.error
|
|
80
|
+
await self.send(FrameReady(prompt=self.state.prompt))
|
|
81
|
+
return outcome.to_output()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`generate()` fails by raising, and the exception reaches `process_output()` as `outcome.error`. Recover an error you expect there and return `None` to go on to the next step. Re-raise anything else to end the session with an error, as the default `process_output()` does.
|
|
85
|
+
|
|
86
|
+
Commands and lifecycle hooks such as `@session_started` and `@connected` run between steps, never during one. A command handler refuses by raising `CommandError(code, message)`, which fails the caller's own request. For a loop that is not one step per emit, override `run()` and write it against `emit()`, `send()`, and `complete_step()`.
|
|
87
|
+
|
|
88
|
+
A session can also start with its setup in hand and run a set number of steps: `POST /start_session` takes a `starting_input` of state values and commands, and `steps`, and the session ends once the model has reported that many. Each step can be kept as a folder of files, turned on in `reactor.yaml`:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
runtime:
|
|
92
|
+
step_results:
|
|
93
|
+
enabled: true
|
|
94
|
+
video: {codec: h264, preset: veryfast, crf: 23} # or codec: h265
|
|
95
|
+
audio: {codec: aac, bitrate_kbps: 128}
|
|
96
|
+
queue: 8
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A step's tracks are saved as one `output.mp4`, with a `result.json` that lists the step's files, and `GET /sessions/{id}/steps` serves the folders.
|
|
100
|
+
|
|
101
|
+
A session can also be stepped: `POST /start_session` with `"generation": "stepped"` runs one step per `RequestStep` a client sends on the data channel instead of streaming, and answers each request with a `StepResult` that carries the step's media, so a stepped client must negotiate data-channel chunking. The model code is the same in both modes; a model that overrides `run()` or `submit_command()` cannot be stepped. The [stepped echo example](./examples/stepped_echo/README.md) answers each step with what it saw, so a client can check every part of its request.
|
|
102
|
+
|
|
103
|
+
A session can join a LiveKit room. The room connection publishes the model's output tracks, and every other participant in the room is a client of the session. The model's `reactor.yaml` says which RPC methods, data topics, and byte streams reach which of its commands, messages, and input tracks:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
runtime:
|
|
107
|
+
livekit:
|
|
108
|
+
rpc:
|
|
109
|
+
- method: lk.clear_buffer # a call to this method runs clear_buffer
|
|
110
|
+
command: clear_buffer
|
|
111
|
+
- method: lk.playback_finished # sending playback_finished calls this method
|
|
112
|
+
message: playback_finished
|
|
113
|
+
topics: [avatar] # a data packet on avatar.<command> runs <command>
|
|
114
|
+
media_inputs:
|
|
115
|
+
lk.audio_stream: agent_audio # byte streams on this topic feed agent_audio
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The LiveKit SDK is an optional part of the runtime, so a model that joins rooms lists `reactor-runtime[livekit]` in its `requirements.txt`. A running session joins a room through a connection of its own:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
POST /sessions/{session_id}/transport/livekit/connections
|
|
122
|
+
{"url": "wss://example.livekit.cloud", "token": "eyJhbGciOi..."}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Scaffold, build, and run it with the CLI:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
reactor init my-model
|
|
129
|
+
cd my-model
|
|
130
|
+
reactor run
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`reactor run` builds a container with the runtime inside and serves WebRTC signaling on port 8080. Point a browser at it with the [JS SDK](https://docs.reactor.inc/sdk-reference/using-the-sdk), or connect from the [Reactor Sandbox](https://reactor-sandbox.vercel.app/) and watch frames stream immediately.
|
|
134
|
+
|
|
135
|
+
Log from the same import, passing context as keyword arguments:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from reactor_runtime import get_logger
|
|
139
|
+
|
|
140
|
+
logger = get_logger(__name__)
|
|
141
|
+
|
|
142
|
+
logger.info("scene changed", prompt=self.prompt)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Records render as `key=value` text, or as one JSON object per line under `REACTOR_LOG_FORMAT=json`. Every record written during a session carries its id and the runtime's lifecycle state, including the records of `logging.getLogger()` and of the libraries your model imports.
|
|
146
|
+
|
|
147
|
+
## Install
|
|
148
|
+
|
|
149
|
+
Everything runs through the [`reactor` CLI](https://docs.reactor.inc/deploy/platform/installation). There is nothing to install on your host but the CLI and Docker; the runtime ships inside the image the CLI builds for your workspace.
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
brew install reactor-team/tools/reactor-cli
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Not on macOS, or pinning a release in CI? See [Install the CLI](https://docs.reactor.inc/deploy/platform/installation).
|
|
156
|
+
|
|
157
|
+
## Learn more
|
|
158
|
+
|
|
159
|
+
- [Quickstart](https://docs.reactor.inc/deploy/development/quickstart): from zero to a model deployed on Reactor's GPUs
|
|
160
|
+
- [Runtime overview](https://docs.reactor.inc/deploy/development/overview): what the runtime handles, and the outline of a model
|
|
161
|
+
- [Model anatomy](https://docs.reactor.inc/deploy/development/reactor-app/model-anatomy): every member of a `ReactorApp`, line by line
|
|
162
|
+
- [The step loop](https://docs.reactor.inc/deploy/development/reactor-app/step-loop): `process_input()`, `generate()`, `process_output()`, and the frame rate
|
|
163
|
+
- [Events and messages](https://docs.reactor.inc/deploy/development/reactor-app/events-and-messages): commands, refusals with `CommandError`, and messages to clients
|
|
164
|
+
- [Video and audio tracks](https://docs.reactor.inc/deploy/development/reactor-app/video-and-audio-tracks): output and input tracks, and how input is buffered
|
|
165
|
+
- [Starter example](./examples/starter/README.md): the model `reactor init` scaffolds: one class, `generate()` alone, the smallest complete `ReactorApp`
|
|
166
|
+
- [Echo example](./examples/echo/README.md): the client's webcam and microphone in, an effect applied, both sent back, in batches
|
|
167
|
+
- [Waypoint example](./examples/waypoint/README.md): a world model on a GPU, seeded from an upload and steered live
|
|
168
|
+
- [Stepped echo example](./examples/stepped_echo/README.md): one frame and a JSON report back for every step, the reference for a stepped session
|
|
169
|
+
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
This repository holds the runtime package itself: the authoring interface, the session runner, the media transport, and the wire protocol. To work on it, use [mise](https://mise.jdx.dev/), which pins the toolchain and forwards every task through a thin `make` shim:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
mise run install # install deps, generate wire bindings, and git hooks
|
|
176
|
+
mise run lint # ruff check, ruff format --check, and mise.lock drift
|
|
177
|
+
mise run format # apply ruff formatting
|
|
178
|
+
mise run typecheck # ty (strict)
|
|
179
|
+
mise run test # unit tests on the floor Python
|
|
180
|
+
mise run test-matrix # unit tests on every supported Python
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
Licensed under the [Apache License, Version 2.0](./LICENSE).
|
|
@@ -10,7 +10,7 @@ module-name = [
|
|
|
10
10
|
namespace = true
|
|
11
11
|
|
|
12
12
|
[tool.reactor-wire]
|
|
13
|
-
version = "1.
|
|
13
|
+
version = "1.20261022.18"
|
|
14
14
|
|
|
15
15
|
[tool.ruff]
|
|
16
16
|
line-length = 100
|
|
@@ -64,6 +64,7 @@ convention = "google"
|
|
|
64
64
|
|
|
65
65
|
[tool.ruff.lint.per-file-ignores]
|
|
66
66
|
"tests/**" = ["D"]
|
|
67
|
+
"endurance-tests/unit/**" = ["D"]
|
|
67
68
|
|
|
68
69
|
[tool.ty.environment]
|
|
69
70
|
python = ".venv"
|
|
@@ -73,14 +74,18 @@ extra-paths = [
|
|
|
73
74
|
"examples/starter",
|
|
74
75
|
"examples/echo",
|
|
75
76
|
"examples/waypoint",
|
|
77
|
+
"examples/stepped_echo",
|
|
76
78
|
"tests/unit/transport/webrtc",
|
|
77
79
|
"tests/contract",
|
|
80
|
+
"endurance-tests",
|
|
81
|
+
"endurance-tests/model",
|
|
78
82
|
]
|
|
79
83
|
|
|
80
84
|
[tool.ty.src]
|
|
81
85
|
include = [
|
|
82
86
|
"src",
|
|
83
87
|
"tests",
|
|
88
|
+
"endurance-tests",
|
|
84
89
|
"examples",
|
|
85
90
|
]
|
|
86
91
|
exclude = [
|
|
@@ -89,13 +94,19 @@ exclude = [
|
|
|
89
94
|
]
|
|
90
95
|
|
|
91
96
|
[tool.pytest.ini_options]
|
|
92
|
-
testpaths = [
|
|
97
|
+
testpaths = [
|
|
98
|
+
"tests",
|
|
99
|
+
"endurance-tests/unit",
|
|
100
|
+
]
|
|
93
101
|
asyncio_mode = "auto"
|
|
94
|
-
pythonpath = [
|
|
102
|
+
pythonpath = [
|
|
103
|
+
".",
|
|
104
|
+
"endurance-tests",
|
|
105
|
+
]
|
|
95
106
|
|
|
96
107
|
[project]
|
|
97
108
|
name = "reactor-runtime"
|
|
98
|
-
version = "3.
|
|
109
|
+
version = "3.9.0"
|
|
99
110
|
description = "A Python framework for building real-time, interactive video models"
|
|
100
111
|
readme = "README.md"
|
|
101
112
|
license = "Apache-2.0"
|
|
@@ -119,7 +130,7 @@ dependencies = [
|
|
|
119
130
|
"prometheus-client>=0.26.0",
|
|
120
131
|
"protobuf>=7.35.1",
|
|
121
132
|
"pyyaml>=6.0.3",
|
|
122
|
-
"reactor-webrtc==0.
|
|
133
|
+
"reactor-webrtc==0.24.0",
|
|
123
134
|
"uvicorn>=0.49.0",
|
|
124
135
|
]
|
|
125
136
|
|
|
@@ -127,14 +138,21 @@ dependencies = [
|
|
|
127
138
|
name = "Reactor"
|
|
128
139
|
email = "team@reactor.inc"
|
|
129
140
|
|
|
141
|
+
[project.optional-dependencies]
|
|
142
|
+
livekit = ["livekit==1.1.21"]
|
|
143
|
+
|
|
130
144
|
[project.urls]
|
|
131
145
|
Source = "https://github.com/reactor-team/reactor-runtime"
|
|
132
146
|
|
|
133
147
|
[dependency-groups]
|
|
134
148
|
dev = [
|
|
149
|
+
"certifi>=2026.10.19",
|
|
135
150
|
"httpx>=0.28.1",
|
|
151
|
+
"livekit==1.1.21",
|
|
152
|
+
"livekit-api>=1.2.2",
|
|
136
153
|
"opencv-python-headless>=4.10",
|
|
137
154
|
"pillow>=10.1",
|
|
155
|
+
"psutil>=7.0",
|
|
138
156
|
"pytest",
|
|
139
157
|
"pytest-asyncio",
|
|
140
158
|
"types-protobuf",
|
|
@@ -16,11 +16,11 @@ namespace = true
|
|
|
16
16
|
# gates the pin against proto/, so a release cannot ship a schema older than the
|
|
17
17
|
# sources it was built from.
|
|
18
18
|
[tool.reactor-wire]
|
|
19
|
-
version = "1.
|
|
19
|
+
version = "1.20261022.18"
|
|
20
20
|
|
|
21
21
|
[project]
|
|
22
22
|
name = "reactor-runtime"
|
|
23
|
-
version = "3.
|
|
23
|
+
version = "3.9.0"
|
|
24
24
|
description = "A Python framework for building real-time, interactive video models"
|
|
25
25
|
readme = "README.md"
|
|
26
26
|
license = "Apache-2.0"
|
|
@@ -42,18 +42,29 @@ dependencies = [
|
|
|
42
42
|
"prometheus-client>=0.26.0",
|
|
43
43
|
"protobuf>=7.35.1",
|
|
44
44
|
"pyyaml>=6.0.3",
|
|
45
|
-
"reactor-webrtc==0.
|
|
45
|
+
"reactor-webrtc==0.24.0",
|
|
46
46
|
"uvicorn>=0.49.0",
|
|
47
47
|
]
|
|
48
48
|
|
|
49
|
+
[project.optional-dependencies]
|
|
50
|
+
# The LiveKit transport. A model that joins LiveKit rooms lists
|
|
51
|
+
# `reactor-runtime[livekit]` in its requirements; the runtime pin stays the
|
|
52
|
+
# CLI's `build.runtime_version`.
|
|
53
|
+
livekit = ["livekit==1.1.21"]
|
|
54
|
+
|
|
49
55
|
[project.urls]
|
|
50
56
|
Source = "https://github.com/reactor-team/reactor-runtime"
|
|
51
57
|
|
|
52
58
|
[dependency-groups]
|
|
53
59
|
dev = [
|
|
60
|
+
"certifi>=2026.10.19",
|
|
54
61
|
"httpx>=0.28.1",
|
|
62
|
+
# The LiveKit transport and the token minting its tests use.
|
|
63
|
+
"livekit==1.1.21",
|
|
64
|
+
"livekit-api>=1.2.2",
|
|
55
65
|
"opencv-python-headless>=4.10",
|
|
56
66
|
"pillow>=10.1",
|
|
67
|
+
"psutil>=7.0",
|
|
57
68
|
"pytest",
|
|
58
69
|
"pytest-asyncio",
|
|
59
70
|
"types-protobuf",
|
|
@@ -121,6 +132,7 @@ convention = "google"
|
|
|
121
132
|
|
|
122
133
|
[tool.ruff.lint.per-file-ignores]
|
|
123
134
|
"tests/**" = ["D"]
|
|
135
|
+
"endurance-tests/unit/**" = ["D"]
|
|
124
136
|
|
|
125
137
|
[tool.ty.environment]
|
|
126
138
|
python = ".venv"
|
|
@@ -131,20 +143,25 @@ python-version = "3.12"
|
|
|
131
143
|
# its tests can import their sibling `conftest` for the shared peer fakes; and
|
|
132
144
|
# the contract suite, so its tests can import their sibling `contract_helpers`;
|
|
133
145
|
# and each example workspace, whose two modules import each other as top-level
|
|
134
|
-
# names, the way the runtime imports a workspace
|
|
146
|
+
# names, the way the runtime imports a workspace; the endurance suite's
|
|
147
|
+
# root, so its `endurance` package resolves; and its model, imported by name.
|
|
135
148
|
extra-paths = [
|
|
136
149
|
".",
|
|
137
150
|
"examples/starter",
|
|
138
151
|
"examples/echo",
|
|
139
152
|
"examples/waypoint",
|
|
153
|
+
"examples/stepped_echo",
|
|
140
154
|
"tests/unit/transport/webrtc",
|
|
141
155
|
"tests/contract",
|
|
156
|
+
"endurance-tests",
|
|
157
|
+
"endurance-tests/model",
|
|
142
158
|
]
|
|
143
159
|
|
|
144
160
|
[tool.ty.src]
|
|
145
|
-
# Check the package, its tests, and the example models;
|
|
146
|
-
# tooling that is not part of the published package
|
|
147
|
-
|
|
161
|
+
# Check the package, its tests, the endurance suite, and the example models;
|
|
162
|
+
# scripts/ ships developer tooling that is not part of the published package
|
|
163
|
+
# surface.
|
|
164
|
+
include = ["src", "tests", "endurance-tests", "examples"]
|
|
148
165
|
# Generated bindings ship their own stubs.
|
|
149
166
|
exclude = [
|
|
150
167
|
"src/reactor_wire",
|
|
@@ -155,8 +172,9 @@ exclude = [
|
|
|
155
172
|
]
|
|
156
173
|
|
|
157
174
|
[tool.pytest.ini_options]
|
|
158
|
-
testpaths = ["tests"]
|
|
175
|
+
testpaths = ["tests", "endurance-tests/unit"]
|
|
159
176
|
asyncio_mode = "auto"
|
|
160
177
|
# The example models live at the repo root (outside the package) so they are not
|
|
161
|
-
# shipped; put the root on the path so the suite can import them.
|
|
162
|
-
|
|
178
|
+
# shipped; put the root on the path so the suite can import them. The endurance
|
|
179
|
+
# suite's root holds its `endurance` package.
|
|
180
|
+
pythonpath = [".", "endurance-tests"]
|
|
@@ -22,6 +22,9 @@ from reactor_runtime.core.model import (
|
|
|
22
22
|
UploadedFile,
|
|
23
23
|
)
|
|
24
24
|
from reactor_runtime.core.service import (
|
|
25
|
+
LiveKitBlockError,
|
|
26
|
+
LiveKitConfig,
|
|
27
|
+
LiveKitRpcMethod,
|
|
25
28
|
RecordingConfig,
|
|
26
29
|
RuntimeConfig,
|
|
27
30
|
ServiceComponent,
|
|
@@ -36,19 +39,26 @@ from reactor_runtime.core.session import (
|
|
|
36
39
|
from reactor_runtime.core.stats import TransportReading, TransportTrackReading
|
|
37
40
|
from reactor_runtime.core.transport import (
|
|
38
41
|
Connection,
|
|
42
|
+
ConnectionOriginSource,
|
|
39
43
|
ConnectionSink,
|
|
40
44
|
FrameStageSource,
|
|
45
|
+
InputTrackWatcher,
|
|
46
|
+
MessageObserver,
|
|
47
|
+
MessageStageSource,
|
|
48
|
+
ReplyRoute,
|
|
41
49
|
TransportStatsSource,
|
|
42
50
|
)
|
|
43
51
|
from reactor_runtime.core.typespec import TypeSpec
|
|
44
52
|
from reactor_runtime.core.values import (
|
|
45
53
|
ClientConnectionStat,
|
|
54
|
+
ClientEnvironment,
|
|
46
55
|
ClientStatsBatch,
|
|
47
56
|
ClientTrackDirection,
|
|
48
57
|
ClientTrackStat,
|
|
49
58
|
CommandFailure,
|
|
50
59
|
CompletedStep,
|
|
51
60
|
ConnectionCapabilities,
|
|
61
|
+
ConnectionOrigin,
|
|
52
62
|
ConnId,
|
|
53
63
|
FrameStage,
|
|
54
64
|
Health,
|
|
@@ -56,7 +66,11 @@ from reactor_runtime.core.values import (
|
|
|
56
66
|
InputFrame,
|
|
57
67
|
MediaBundle,
|
|
58
68
|
MediaChunk,
|
|
69
|
+
MessageStage,
|
|
70
|
+
RequestedStep,
|
|
59
71
|
RuntimeState,
|
|
72
|
+
StepRefusal,
|
|
73
|
+
StepRefusalKind,
|
|
60
74
|
TrackData,
|
|
61
75
|
TrackDirection,
|
|
62
76
|
TrackInfo,
|
|
@@ -68,6 +82,7 @@ __all__ = [
|
|
|
68
82
|
"ClientConnected",
|
|
69
83
|
"ClientConnectionStat",
|
|
70
84
|
"ClientDisconnected",
|
|
85
|
+
"ClientEnvironment",
|
|
71
86
|
"ClientStatsBatch",
|
|
72
87
|
"ClientTrackDirection",
|
|
73
88
|
"ClientTrackStat",
|
|
@@ -78,6 +93,8 @@ __all__ = [
|
|
|
78
93
|
"ConnId",
|
|
79
94
|
"Connection",
|
|
80
95
|
"ConnectionCapabilities",
|
|
96
|
+
"ConnectionOrigin",
|
|
97
|
+
"ConnectionOriginSource",
|
|
81
98
|
"ConnectionSink",
|
|
82
99
|
"EndReason",
|
|
83
100
|
"FieldInfo",
|
|
@@ -88,10 +105,19 @@ __all__ = [
|
|
|
88
105
|
"HealthStatus",
|
|
89
106
|
"InputField",
|
|
90
107
|
"InputFrame",
|
|
108
|
+
"InputTrackWatcher",
|
|
109
|
+
"LiveKitBlockError",
|
|
110
|
+
"LiveKitConfig",
|
|
111
|
+
"LiveKitRpcMethod",
|
|
91
112
|
"MediaBundle",
|
|
92
113
|
"MediaChunk",
|
|
114
|
+
"MessageObserver",
|
|
115
|
+
"MessageStage",
|
|
116
|
+
"MessageStageSource",
|
|
93
117
|
"ReactorEvent",
|
|
94
118
|
"RecordingConfig",
|
|
119
|
+
"ReplyRoute",
|
|
120
|
+
"RequestedStep",
|
|
95
121
|
"RuntimeConfig",
|
|
96
122
|
"RuntimeState",
|
|
97
123
|
"ServiceComponent",
|
|
@@ -100,6 +126,8 @@ __all__ = [
|
|
|
100
126
|
"SessionStarted",
|
|
101
127
|
"SessionState",
|
|
102
128
|
"StartingInputApplied",
|
|
129
|
+
"StepRefusal",
|
|
130
|
+
"StepRefusalKind",
|
|
103
131
|
"StepResultsConfig",
|
|
104
132
|
"TrackData",
|
|
105
133
|
"TrackDirection",
|