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.
Files changed (150) hide show
  1. reactor_runtime-3.9.0/PKG-INFO +214 -0
  2. reactor_runtime-3.9.0/README.md +185 -0
  3. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/pyproject.toml +23 -5
  4. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/pyproject.toml.orig +28 -10
  5. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/__init__.py +28 -0
  6. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/model.py +6 -0
  7. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/service.py +94 -0
  8. reactor_runtime-3.9.0/src/reactor_runtime/core/stages.py +71 -0
  9. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/stats.py +4 -1
  10. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/transport.py +182 -5
  11. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/values.py +187 -15
  12. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/server.py +5 -1
  13. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/spec.py +8 -5
  14. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/reactor_app.py +325 -26
  15. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/errors.py +4 -0
  16. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/messages.py +5 -4
  17. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/bridge.py +130 -12
  18. reactor_runtime-3.9.0/src/reactor_runtime/interface/internal/input_buffer.py +327 -0
  19. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/reactor_core.py +242 -20
  20. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/reactor_pipeline.py +4 -1
  21. reactor_runtime-3.9.0/src/reactor_runtime/interface/tracks/descriptors.py +117 -0
  22. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/manifest.py +123 -7
  23. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/message_gateway.py +77 -2
  24. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/base.py +20 -1
  25. reactor_runtime-3.9.0/src/reactor_runtime/protocol/frames.py +308 -0
  26. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/client_stats.py +62 -3
  27. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/connection_manager.py +310 -67
  28. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/offer_epochs.py +5 -0
  29. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/runner.py +970 -80
  30. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/runtime_stats.py +74 -4
  31. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/session_start.py +36 -2
  32. reactor_runtime-3.9.0/src/reactor_runtime/runner/stages.py +154 -0
  33. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/system_client.py +22 -7
  34. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/schema.py +12 -3
  35. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/serve.py +11 -3
  36. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/__init__.py +2 -2
  37. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/mp4.py +17 -4
  38. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/store.py +7 -2
  39. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/__init__.py +2 -0
  40. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/__init__.py +11 -0
  41. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/connection.py +267 -0
  42. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/media_in.py +488 -0
  43. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/pacer.py +253 -0
  44. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/participant.py +580 -0
  45. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/room.py +556 -0
  46. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/router.py +293 -0
  47. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/signals.py +77 -0
  48. reactor_runtime-3.9.0/src/reactor_runtime/transport/livekit/version.py +9 -0
  49. reactor_runtime-3.9.0/src/reactor_runtime/transport/router.py +331 -0
  50. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/client_stats.py +73 -14
  51. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/connection.py +43 -4
  52. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/frame_stages.py +31 -22
  53. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/frames.py +40 -0
  54. reactor_runtime-3.9.0/src/reactor_runtime/transport/webrtc/message_stages.py +120 -0
  55. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/pacer.py +92 -28
  56. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/peer.py +352 -34
  57. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/reading.py +24 -0
  58. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/router.py +52 -58
  59. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/signaling.py +16 -1
  60. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/stats.py +63 -0
  61. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/version.py +7 -1
  62. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/data_pb2.py +6 -5
  63. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/data_pb2.pyi +9 -4
  64. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/platform_pb2.py +24 -20
  65. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/platform_pb2.pyi +44 -6
  66. reactor_runtime-3.9.0/src/reactor_wire/v1/step_pb2.py +51 -0
  67. reactor_runtime-3.9.0/src/reactor_wire/v1/step_pb2.pyi +103 -0
  68. reactor_runtime-3.8.2/PKG-INFO +0 -201
  69. reactor_runtime-3.8.2/README.md +0 -174
  70. reactor_runtime-3.8.2/src/reactor_runtime/interface/internal/input_buffer.py +0 -195
  71. reactor_runtime-3.8.2/src/reactor_runtime/interface/tracks/descriptors.py +0 -62
  72. reactor_runtime-3.8.2/src/reactor_runtime/transport/router.py +0 -159
  73. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/LICENSE +0 -0
  74. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/NOTICE +0 -0
  75. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/__init__.py +0 -0
  76. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/codes.py +0 -0
  77. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/fields.py +0 -0
  78. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/naming.py +0 -0
  79. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/session.py +0 -0
  80. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/core/typespec.py +0 -0
  81. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/__init__.py +0 -0
  82. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/errors.py +0 -0
  83. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/ipc.py +0 -0
  84. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/protocol.py +0 -0
  85. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/rank.py +0 -0
  86. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/runner.py +0 -0
  87. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/distributed/worker.py +0 -0
  88. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/event_stream.py +0 -0
  89. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/__init__.py +0 -0
  90. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/events.py +0 -0
  91. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/routes.py +0 -0
  92. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/http/step_usage.py +0 -0
  93. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/__init__.py +0 -0
  94. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/__init__.py +0 -0
  95. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/input_state.py +0 -0
  96. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/outcome.py +0 -0
  97. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/app/step_completed.py +0 -0
  98. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/client.py +0 -0
  99. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/__init__.py +0 -0
  100. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/events/decorators.py +0 -0
  101. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/__init__.py +0 -0
  102. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/internal/aliases.py +0 -0
  103. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/__init__.py +0 -0
  104. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/contract.py +0 -0
  105. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/reactor_model.py +0 -0
  106. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/model/schema.py +0 -0
  107. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/__init__.py +0 -0
  108. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/idle.py +0 -0
  109. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/pipeline/input_state.py +0 -0
  110. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/tracks/__init__.py +0 -0
  111. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/tracks/input.py +0 -0
  112. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/interface/tracks/output.py +0 -0
  113. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/log.py +0 -0
  114. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/__init__.py +0 -0
  115. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/command.py +0 -0
  116. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/model.py +0 -0
  117. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/registry.py +0 -0
  118. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/metrics/session.py +0 -0
  119. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/paths.py +0 -0
  120. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/__init__.py +0 -0
  121. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/common.py +0 -0
  122. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v0/__init__.py +0 -0
  123. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v0/codec.py +0 -0
  124. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v1/__init__.py +0 -0
  125. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/protocol/v1/codec.py +0 -0
  126. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/py.typed +0 -0
  127. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/__init__.py +0 -0
  128. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/chunk_encoder.py +0 -0
  129. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/markers.py +0 -0
  130. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/recording/recorder.py +0 -0
  131. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/__init__.py +0 -0
  132. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/state_machine.py +0 -0
  133. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/runner/upload_resolution.py +0 -0
  134. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/service.py +0 -0
  135. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/step_results/usage.py +0 -0
  136. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/acceptor.py +0 -0
  137. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/__init__.py +0 -0
  138. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/acceptor.py +0 -0
  139. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/config.py +0 -0
  140. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/metrics.py +0 -0
  141. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/transport/webrtc/sdp.py +0 -0
  142. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_runtime/upload_store.py +0 -0
  143. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/common_pb2.py +0 -0
  144. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/common_pb2.pyi +0 -0
  145. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/control_pb2.py +0 -0
  146. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/control_pb2.pyi +0 -0
  147. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/model_pb2.py +0 -0
  148. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/model_pb2.pyi +0 -0
  149. {reactor_runtime-3.8.2 → reactor_runtime-3.9.0}/src/reactor_wire/v1/track_pb2.py +0 -0
  150. {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.20261013.10"
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 = ["tests"]
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.8.2"
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.22.1",
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.20261013.10"
19
+ version = "1.20261022.18"
20
20
 
21
21
  [project]
22
22
  name = "reactor-runtime"
23
- version = "3.8.2"
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.22.1",
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; scripts/ ships developer
146
- # tooling that is not part of the published package surface.
147
- include = ["src", "tests", "examples"]
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
- pythonpath = ["."]
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",