reactor-runtime 2.3.2__tar.gz → 2.5.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-2.3.2 → reactor_runtime-2.5.0}/PKG-INFO +22 -3
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/README.md +20 -1
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/pyproject.toml +9 -4
- reactor_runtime-2.5.0/src/reactor_cli/commands/init.py +155 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/commands/run.py +35 -8
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/commands/schema.py +11 -3
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/main.py +1 -1
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/__init__.py +4 -0
- reactor_runtime-2.5.0/src/reactor_runtime/config.py +170 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/__init__.py +6 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/driver/pipeline_executor.py +24 -6
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/events/messages.py +10 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/internal/input_buffer.py +33 -20
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/internal/reactor_core.py +11 -3
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/runtime_api.py +125 -3
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/runtimes/headless/input_feeder.py +11 -2
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/runtimes/http/http_runtime.py +81 -8
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/runtimes/http/types.py +37 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/schema.py +71 -5
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/__init__.py +6 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/aiortc/audio_track.py +148 -2
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/aiortc/client.py +144 -4
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/events.py +23 -3
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/client.py +185 -37
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/__init__.py +2 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/factory.py +5 -1
- reactor_runtime-2.5.0/src/reactor_runtime/transports/gstreamer/decoders/opus.py +54 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/receiver/audio.py +15 -16
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/interface.py +20 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/media.py +43 -0
- reactor_runtime-2.5.0/src/reactor_runtime/utils/paths.py +83 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime.egg-info/PKG-INFO +22 -3
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime.egg-info/SOURCES.txt +4 -1
- reactor_runtime-2.5.0/src/reactor_runtime.egg-info/entry_points.txt +2 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime.egg-info/requires.txt +1 -1
- reactor_runtime-2.5.0/src/template/Dockerfile +66 -0
- reactor_runtime-2.5.0/src/template/README.md +85 -0
- reactor_runtime-2.3.2/src/template/reactor.yaml → reactor_runtime-2.5.0/src/template/config.yaml +3 -3
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/template/model.py +1 -1
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/template/pipeline.py +1 -1
- reactor_runtime-2.5.0/src/template/reactor.yaml +7 -0
- reactor_runtime-2.5.0/src/template/requirements.txt +16 -0
- reactor_runtime-2.3.2/src/reactor_cli/commands/init.py +0 -90
- reactor_runtime-2.3.2/src/reactor_runtime/config.py +0 -49
- reactor_runtime-2.3.2/src/reactor_runtime.egg-info/entry_points.txt +0 -2
- reactor_runtime-2.3.2/src/template/README.md +0 -45
- reactor_runtime-2.3.2/src/template/config.yml +0 -3
- reactor_runtime-2.3.2/src/template/requirements.txt +0 -2
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/setup.cfg +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/api/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/commands/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/utils/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/utils/config.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/utils/runtime.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_cli/utils/version.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/defaults.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/driver/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/driver/step_result.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/events/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/events/connected.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/events/event.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/events/upload.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/internal/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/internal/output_buffer.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/model/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/model/decorators.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/model/handlers.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/model/reactor_model.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/pipeline/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/pipeline/idle.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/pipeline/input_state.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/pipeline/reactor_pipeline.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/tracks/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/tracks/descriptors.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/tracks/input.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/tracks/output.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/upload.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/model_state.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/backends/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/backends/base.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/backends/file.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/backends/otlp.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/helpers.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/plotting/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/plotting/plot_profiling.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/profiler.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/profiling/singleton.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/runtimes/headless/config.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/runtimes/headless/headless_runtime.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/runtimes/http/config.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/schema_validator.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/aiortc/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/aiortc/frame_conversion.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/aiortc/ice_connection.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/aiortc/video_track.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/config.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/av1.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/base.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/h264.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/h265.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/vp8.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/decoders/vp9.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/av1.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/base.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/factory.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/h264.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/h265.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/opus.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/vp8.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/encoders/vp9.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/gst.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/gst_helpers.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/probes/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/probes/fps_probe.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/receiver/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/receiver/base.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/receiver/video.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sdp/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sdp/bundle.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sdp/codec.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sdp/extmap.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sdp/ice.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sender/__init__.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sender/audio.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sender/base.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/sender/video.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/settings.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/gstreamer/signals.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/ice_uris.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/transports/types.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/utils/launch.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/utils/loader.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/utils/log.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/utils/messages.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/utils/typing.py +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime.egg-info/dependency_links.txt +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime.egg-info/top_level.txt +0 -0
- {reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/template/__init__.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reactor_runtime
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: Reactor runtime with public model API
|
|
5
5
|
Author-email: Reactor <team@reactor.inc>
|
|
6
6
|
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
|
-
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: numpy>=1.24.0
|
|
9
9
|
Requires-Dist: pydantic>=2.0.0
|
|
10
10
|
Requires-Dist: omegaconf>=2.3.0
|
|
11
11
|
Requires-Dist: av>=14.0.0
|
|
@@ -68,4 +68,23 @@ Implement `inference()` as a generator that yields frames. Clients can update `s
|
|
|
68
68
|
- **Real-time streaming** — frames delivered over WebRTC as they're generated
|
|
69
69
|
- **Live interaction** — clients change inputs mid-generation via typed, validated state
|
|
70
70
|
- **No transport code** — no WebRTC, WebSocket, or video encoding to manage
|
|
71
|
-
- **Scaffold & run** — `reactor
|
|
71
|
+
- **Scaffold & run** — the Go `reactor` CLI gives you `reactor init` / `reactor run`; the
|
|
72
|
+
Python runtime ships its own `reactor-runtime` script for standalone use
|
|
73
|
+
|
|
74
|
+
## Standalone usage (no Go CLI)
|
|
75
|
+
|
|
76
|
+
The runtime CLI is installed as `reactor-runtime` so it never shadows the Go
|
|
77
|
+
`reactor` binary. If you only use the Python runtime:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install reactor-runtime
|
|
81
|
+
reactor-runtime init my-model
|
|
82
|
+
cd my-model
|
|
83
|
+
python -m venv .venv && . .venv/bin/activate
|
|
84
|
+
pip install -r requirements.txt
|
|
85
|
+
reactor-runtime run
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The Go `reactor` CLI delegates to this same `reactor-runtime` script under the
|
|
89
|
+
hood; both `reactor run` (Go) and `reactor-runtime run` (standalone) execute the
|
|
90
|
+
same Python entry point.
|
|
@@ -41,4 +41,23 @@ Implement `inference()` as a generator that yields frames. Clients can update `s
|
|
|
41
41
|
- **Real-time streaming** — frames delivered over WebRTC as they're generated
|
|
42
42
|
- **Live interaction** — clients change inputs mid-generation via typed, validated state
|
|
43
43
|
- **No transport code** — no WebRTC, WebSocket, or video encoding to manage
|
|
44
|
-
- **Scaffold & run** — `reactor
|
|
44
|
+
- **Scaffold & run** — the Go `reactor` CLI gives you `reactor init` / `reactor run`; the
|
|
45
|
+
Python runtime ships its own `reactor-runtime` script for standalone use
|
|
46
|
+
|
|
47
|
+
## Standalone usage (no Go CLI)
|
|
48
|
+
|
|
49
|
+
The runtime CLI is installed as `reactor-runtime` so it never shadows the Go
|
|
50
|
+
`reactor` binary. If you only use the Python runtime:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install reactor-runtime
|
|
54
|
+
reactor-runtime init my-model
|
|
55
|
+
cd my-model
|
|
56
|
+
python -m venv .venv && . .venv/bin/activate
|
|
57
|
+
pip install -r requirements.txt
|
|
58
|
+
reactor-runtime run
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The Go `reactor` CLI delegates to this same `reactor-runtime` script under the
|
|
62
|
+
hood; both `reactor run` (Go) and `reactor-runtime run` (standalone) execute the
|
|
63
|
+
same Python entry point.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "reactor_runtime"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.5.0"
|
|
8
8
|
description = "Reactor runtime with public model API"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Reactor", email = "team@reactor.inc" }
|
|
@@ -13,7 +13,7 @@ readme = "README.md"
|
|
|
13
13
|
requires-python = ">=3.9"
|
|
14
14
|
|
|
15
15
|
dependencies = [
|
|
16
|
-
"numpy
|
|
16
|
+
"numpy>=1.24.0",
|
|
17
17
|
"pydantic>=2.0.0",
|
|
18
18
|
"omegaconf>=2.3.0",
|
|
19
19
|
"av>=14.0.0",
|
|
@@ -38,10 +38,15 @@ gst = [
|
|
|
38
38
|
]
|
|
39
39
|
|
|
40
40
|
[project.scripts]
|
|
41
|
-
reactor
|
|
41
|
+
# The runtime ships a CLI for standalone use (no Go reactor-cli required).
|
|
42
|
+
# Named `reactor-runtime` so it never collides with the Go `reactor` binary,
|
|
43
|
+
# which delegates to this script when both are installed.
|
|
44
|
+
reactor-runtime = "reactor_cli.main:main"
|
|
42
45
|
|
|
43
46
|
[tool.setuptools.package-data]
|
|
44
|
-
|
|
47
|
+
# Dockerfile is included so the scaffolded workspace ships a partner-ready
|
|
48
|
+
# image baseline. The literal name (no extension) requires an explicit entry.
|
|
49
|
+
template = ["*.yaml", "*.yml", "*.txt", "*.md", "Dockerfile"]
|
|
45
50
|
|
|
46
51
|
[tool.reactor-proto]
|
|
47
52
|
# Proto dependency from reactor-team/reactor-proto
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Copyright (c) 2026 Reactor Technologies, Inc. All rights reserved.
|
|
2
|
+
"""Init command — scaffolds a new Reactor model workspace."""
|
|
3
|
+
|
|
4
|
+
import pathlib
|
|
5
|
+
import importlib.resources
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_TEMPLATE_FILES = {
|
|
9
|
+
"pipeline.py": "pipeline.py",
|
|
10
|
+
"model.py": "model.py",
|
|
11
|
+
"reactor.yaml": "reactor.yaml",
|
|
12
|
+
"config.yaml": "config.yaml",
|
|
13
|
+
"requirements.txt": "requirements.txt",
|
|
14
|
+
"README.md": "README.md",
|
|
15
|
+
# Partner-facing Dockerfile baseline (python:3.12-slim + GStreamer apt
|
|
16
|
+
# deps + reactor-runtime run). Listed last so the printed file order
|
|
17
|
+
# mirrors the order partners typically read them in.
|
|
18
|
+
"Dockerfile": "Dockerfile",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InitCommand:
|
|
23
|
+
@staticmethod
|
|
24
|
+
def register_subcommand(subparsers):
|
|
25
|
+
"""Register init command"""
|
|
26
|
+
init_parser = subparsers.add_parser(
|
|
27
|
+
"init", help="Initialize a new reactor model workspace"
|
|
28
|
+
)
|
|
29
|
+
init_parser.add_argument(
|
|
30
|
+
"name",
|
|
31
|
+
nargs="?",
|
|
32
|
+
default=None,
|
|
33
|
+
help=(
|
|
34
|
+
"Name of the model. When provided, a new directory with "
|
|
35
|
+
"this name is created in the current folder. When omitted, "
|
|
36
|
+
"the current folder is used (must be empty)."
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
# --cli is set by the Go `reactor` CLI when it forwards to this
|
|
40
|
+
# Python init via `runtime.PythonRun`. The Go side then prints its
|
|
41
|
+
# own next-steps message tailored to the CLI workflow (workspace
|
|
42
|
+
# .venv was created, `reactor run` picks it up, etc.), so when
|
|
43
|
+
# this flag is present we suppress the standalone post-init
|
|
44
|
+
# message here to avoid duplicate / contradictory guidance.
|
|
45
|
+
# Behavior is otherwise identical — only the trailing print block
|
|
46
|
+
# is gated on this flag.
|
|
47
|
+
init_parser.add_argument(
|
|
48
|
+
"--cli",
|
|
49
|
+
action="store_true",
|
|
50
|
+
default=False,
|
|
51
|
+
help="Reserved for use by the reactor CLI; suppresses the standalone post-init message.",
|
|
52
|
+
)
|
|
53
|
+
init_parser.set_defaults(func=InitCommand)
|
|
54
|
+
|
|
55
|
+
def __init__(self, args):
|
|
56
|
+
self.args = args
|
|
57
|
+
|
|
58
|
+
def run(self):
|
|
59
|
+
"""Initialize a Reactor model workspace.
|
|
60
|
+
|
|
61
|
+
Two modes:
|
|
62
|
+
* ``reactor-runtime init <name>``: create a new ``<name>`` directory
|
|
63
|
+
under the current folder and scaffold into it.
|
|
64
|
+
* ``reactor-runtime init`` (no arg): scaffold into the current
|
|
65
|
+
folder. The folder must be empty (modulo dotfiles) so we never
|
|
66
|
+
clobber anything by accident.
|
|
67
|
+
"""
|
|
68
|
+
if self.args.name:
|
|
69
|
+
model_name = self.args.name
|
|
70
|
+
target_dir = pathlib.Path.cwd() / model_name
|
|
71
|
+
if target_dir.exists():
|
|
72
|
+
print(f"Error: Directory '{model_name}' already exists")
|
|
73
|
+
print("Please choose a different name or remove the existing directory")
|
|
74
|
+
return
|
|
75
|
+
try:
|
|
76
|
+
target_dir.mkdir()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
print(f"Error creating directory '{model_name}': {e}")
|
|
79
|
+
return
|
|
80
|
+
else:
|
|
81
|
+
target_dir = pathlib.Path.cwd()
|
|
82
|
+
model_name = target_dir.name
|
|
83
|
+
visible = [p for p in target_dir.iterdir() if not p.name.startswith(".")]
|
|
84
|
+
if visible:
|
|
85
|
+
# The Go `reactor` CLI streams our stdout verbatim when it
|
|
86
|
+
# forwards via PythonRun (--cli), so the retry hint needs
|
|
87
|
+
# to use whichever entrypoint the user is actually driving:
|
|
88
|
+
# `reactor` for the Go-CLI flow (standalone `reactor-runtime`
|
|
89
|
+
# may not even be on PATH), `reactor-runtime` otherwise.
|
|
90
|
+
cli_name = "reactor" if self.args.cli else "reactor-runtime"
|
|
91
|
+
print(
|
|
92
|
+
f"Error: current folder ({target_dir}) is not empty. "
|
|
93
|
+
f"Pass a name (e.g. `{cli_name} init my-model`) or "
|
|
94
|
+
"run from an empty folder."
|
|
95
|
+
)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
import template as template_package
|
|
100
|
+
except ImportError:
|
|
101
|
+
print(
|
|
102
|
+
"Error: template package not found. "
|
|
103
|
+
"Make sure reactor_runtime is properly installed."
|
|
104
|
+
)
|
|
105
|
+
if self.args.name:
|
|
106
|
+
target_dir.rmdir()
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
created = []
|
|
110
|
+
for dest_name, src_name in _TEMPLATE_FILES.items():
|
|
111
|
+
dest_path = target_dir / dest_name
|
|
112
|
+
try:
|
|
113
|
+
content = (
|
|
114
|
+
importlib.resources.files(template_package)
|
|
115
|
+
.joinpath(src_name)
|
|
116
|
+
.read_text(encoding="utf-8")
|
|
117
|
+
)
|
|
118
|
+
if dest_name == "reactor.yaml":
|
|
119
|
+
content = content.replace("name: my-model", f"name: {model_name}")
|
|
120
|
+
dest_path.write_text(content)
|
|
121
|
+
created.append(dest_name)
|
|
122
|
+
except FileNotFoundError:
|
|
123
|
+
print(f"Warning: template file {src_name} not found, skipping")
|
|
124
|
+
|
|
125
|
+
if not created:
|
|
126
|
+
print("Error: no template files were created")
|
|
127
|
+
if self.args.name:
|
|
128
|
+
target_dir.rmdir()
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
for name in created:
|
|
132
|
+
print(f" {name}")
|
|
133
|
+
|
|
134
|
+
print(f"\nReactor workspace '{model_name}' created.")
|
|
135
|
+
|
|
136
|
+
# When invoked from the Go reactor CLI (--cli), the caller emits
|
|
137
|
+
# its own next-steps message that knows about the workspace .venv
|
|
138
|
+
# it just created and the `reactor run` / `reactor model register`
|
|
139
|
+
# commands. We stay silent here so the user only sees one
|
|
140
|
+
# consistent block of guidance.
|
|
141
|
+
if self.args.cli:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Standalone path: partners using `pip install reactor-runtime`
|
|
145
|
+
# directly. They don't have a Go CLI to lean on, so spell out the
|
|
146
|
+
# full workspace-venv provisioning recipe.
|
|
147
|
+
if self.args.name:
|
|
148
|
+
print(f"\n cd {model_name}")
|
|
149
|
+
print(" # With uv (https://docs.astral.sh/uv/ — recommended):")
|
|
150
|
+
print(" uv venv .venv && . .venv/bin/activate")
|
|
151
|
+
print(" uv pip install -r requirements.txt")
|
|
152
|
+
print(" # Or with plain pip:")
|
|
153
|
+
print(" python -m venv .venv && . .venv/bin/activate")
|
|
154
|
+
print(" pip install -r requirements.txt")
|
|
155
|
+
print(" reactor-runtime run\n")
|
|
@@ -20,6 +20,7 @@ from reactor_runtime.transports.config import (
|
|
|
20
20
|
)
|
|
21
21
|
from reactor_runtime.utils.launch import run_reactor_runtime
|
|
22
22
|
from reactor_runtime.utils.log import get_logger
|
|
23
|
+
from reactor_runtime.utils.paths import _set_configured_weights_path
|
|
23
24
|
|
|
24
25
|
logger = get_logger(__name__)
|
|
25
26
|
|
|
@@ -102,22 +103,30 @@ class RunCommand:
|
|
|
102
103
|
if not reactor_yaml_path.exists():
|
|
103
104
|
print(
|
|
104
105
|
f"Error: {_REACTOR_YAML} not found in {model_path}.\n"
|
|
105
|
-
f"Create a {_REACTOR_YAML} with at least
|
|
106
|
+
f"Create a {_REACTOR_YAML} with at least:\n"
|
|
107
|
+
" runtime:\n"
|
|
108
|
+
" import: module:ClassName"
|
|
106
109
|
)
|
|
107
110
|
return
|
|
108
111
|
|
|
109
112
|
raw_yaml = OmegaConf.to_container(
|
|
110
113
|
OmegaConf.load(str(reactor_yaml_path)), resolve=True
|
|
111
114
|
)
|
|
112
|
-
if not isinstance(raw_yaml, dict)
|
|
115
|
+
if not isinstance(raw_yaml, dict):
|
|
116
|
+
print(f"Error: {_REACTOR_YAML} must be a YAML mapping")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
reactor_config = ReactorConfig.from_dict(raw_yaml)
|
|
121
|
+
except KeyError:
|
|
113
122
|
print(
|
|
114
|
-
f"Error: {_REACTOR_YAML} must
|
|
115
|
-
"(e.g.
|
|
123
|
+
f"Error: {_REACTOR_YAML} must declare a model entrypoint, "
|
|
124
|
+
"either as 'runtime.import' (e.g. 'runtime:\\n import: "
|
|
125
|
+
"my_module:MyModel') or, for legacy configs, as a top-level "
|
|
126
|
+
"'model: my_module:MyModel' field."
|
|
116
127
|
)
|
|
117
128
|
return
|
|
118
129
|
|
|
119
|
-
reactor_config = ReactorConfig.from_dict(raw_yaml)
|
|
120
|
-
|
|
121
130
|
# ---------------------------------------------------------
|
|
122
131
|
# Model name (from YAML or env override)
|
|
123
132
|
# ---------------------------------------------------------
|
|
@@ -148,7 +157,7 @@ class RunCommand:
|
|
|
148
157
|
if model_overrides and not reactor_config.config:
|
|
149
158
|
print(
|
|
150
159
|
"Error: --model.* overrides require a 'config' field in "
|
|
151
|
-
f"{_REACTOR_YAML} (e.g. config: config.
|
|
160
|
+
f"{_REACTOR_YAML} (e.g. config: config.yaml)"
|
|
152
161
|
)
|
|
153
162
|
return
|
|
154
163
|
|
|
@@ -157,6 +166,21 @@ class RunCommand:
|
|
|
157
166
|
reactor_config.config = str(model_path / reactor_config.config)
|
|
158
167
|
reactor_config.config_overrides = model_overrides
|
|
159
168
|
|
|
169
|
+
# ---------------------------------------------------------
|
|
170
|
+
# Wire reactor.yaml's weights_path into the get_weights_path()
|
|
171
|
+
# helper so model code can call it with no arguments and still
|
|
172
|
+
# see the YAML override. Relative values are resolved against the
|
|
173
|
+
# model root, mirroring how `runtime.config` is resolved above —
|
|
174
|
+
# partners write `runtime.weights_path: ./weights` and have it
|
|
175
|
+
# work no matter which cwd `reactor-runtime run` is invoked from.
|
|
176
|
+
# ~ expansion happens lazily inside get_weights_path().
|
|
177
|
+
# ---------------------------------------------------------
|
|
178
|
+
if reactor_config.weights_path:
|
|
179
|
+
resolved = reactor_config.weights_path
|
|
180
|
+
if not resolved.startswith("~") and not Path(resolved).is_absolute():
|
|
181
|
+
resolved = str(model_path / resolved)
|
|
182
|
+
_set_configured_weights_path(resolved)
|
|
183
|
+
|
|
160
184
|
# Handle --config-help
|
|
161
185
|
if self.args.config_help:
|
|
162
186
|
if reactor_config.config:
|
|
@@ -179,8 +203,11 @@ class RunCommand:
|
|
|
179
203
|
runtime_parser = config_class.parser()
|
|
180
204
|
|
|
181
205
|
if self.args.help:
|
|
206
|
+
# Show the standalone command name (`reactor-runtime run`) since
|
|
207
|
+
# `--help` is what partners not using the Go CLI most often hit;
|
|
208
|
+
# the Go `reactor run` passthrough delegates here transparently.
|
|
182
209
|
print(
|
|
183
|
-
"Usage: reactor run [--path DIR] [--runtime NAME] [-v/--verbose] [--debug] [runtime options]"
|
|
210
|
+
"Usage: reactor-runtime run [--path DIR] [--runtime NAME] [-v/--verbose] [--debug] [runtime options]"
|
|
184
211
|
)
|
|
185
212
|
print("\nBase options:")
|
|
186
213
|
print(" --path, -p DIR Path to model directory (default: cwd)")
|
|
@@ -96,14 +96,22 @@ class SchemaCommand:
|
|
|
96
96
|
raw_yaml = OmegaConf.to_container(
|
|
97
97
|
OmegaConf.load(str(reactor_yaml_path)), resolve=True
|
|
98
98
|
)
|
|
99
|
-
if not isinstance(raw_yaml, dict)
|
|
99
|
+
if not isinstance(raw_yaml, dict):
|
|
100
100
|
print(
|
|
101
|
-
f"Error: {_REACTOR_YAML} must
|
|
101
|
+
f"Error: {_REACTOR_YAML} must be a YAML mapping",
|
|
102
102
|
file=sys.stderr,
|
|
103
103
|
)
|
|
104
104
|
sys.exit(1)
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
try:
|
|
107
|
+
reactor_config = ReactorConfig.from_dict(raw_yaml)
|
|
108
|
+
except KeyError:
|
|
109
|
+
print(
|
|
110
|
+
f"Error: {_REACTOR_YAML} must declare a model entrypoint via "
|
|
111
|
+
"'runtime.import' (or, for legacy configs, top-level 'model').",
|
|
112
|
+
file=sys.stderr,
|
|
113
|
+
)
|
|
114
|
+
sys.exit(1)
|
|
107
115
|
|
|
108
116
|
add_import_paths([str(model_path)])
|
|
109
117
|
|
|
@@ -14,6 +14,7 @@ from reactor_runtime.interface import (
|
|
|
14
14
|
MESSAGE_REGISTRY,
|
|
15
15
|
FieldInfo,
|
|
16
16
|
InputField,
|
|
17
|
+
InputFrame,
|
|
17
18
|
Output,
|
|
18
19
|
Input,
|
|
19
20
|
Video,
|
|
@@ -27,6 +28,7 @@ from reactor_runtime.interface import (
|
|
|
27
28
|
disconnected,
|
|
28
29
|
)
|
|
29
30
|
from reactor_runtime.profiling.singleton import get_profiler
|
|
31
|
+
from reactor_runtime.utils.paths import get_weights_path
|
|
30
32
|
|
|
31
33
|
try:
|
|
32
34
|
__version__ = version("reactor_runtime")
|
|
@@ -45,6 +47,7 @@ __all__ = [
|
|
|
45
47
|
"MESSAGE_REGISTRY",
|
|
46
48
|
"FieldInfo",
|
|
47
49
|
"InputField",
|
|
50
|
+
"InputFrame",
|
|
48
51
|
"Output",
|
|
49
52
|
"Input",
|
|
50
53
|
"Video",
|
|
@@ -57,5 +60,6 @@ __all__ = [
|
|
|
57
60
|
"connected",
|
|
58
61
|
"disconnected",
|
|
59
62
|
"get_profiler",
|
|
63
|
+
"get_weights_path",
|
|
60
64
|
"__version__",
|
|
61
65
|
]
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Copyright (c) 2026 Reactor Technologies, Inc. All rights reserved.
|
|
2
|
+
"""
|
|
3
|
+
Typed configuration parsed from ``reactor.yaml``.
|
|
4
|
+
|
|
5
|
+
:class:`ReactorConfig` is the runtime-level configuration that identifies
|
|
6
|
+
the model entry point and slug. Track topology is inferred from the
|
|
7
|
+
model class via the ``OUTPUT_REGISTRY`` / ``INPUT_REGISTRY``.
|
|
8
|
+
|
|
9
|
+
Emission settings (``fps``, ``buffer_size``) are declared as class
|
|
10
|
+
attributes on the model class, not in YAML. Model-specific parameters
|
|
11
|
+
(latent dims, learning rate, etc.) live in a separate config file
|
|
12
|
+
pointed to by ``ReactorConfig.config``.
|
|
13
|
+
|
|
14
|
+
Two YAML shapes are supported:
|
|
15
|
+
|
|
16
|
+
* Modern (richer ``reactor.yaml`` shared with the ``reactor`` CLI)::
|
|
17
|
+
|
|
18
|
+
model:
|
|
19
|
+
name: my-model # slug, optional
|
|
20
|
+
# ...other CLI-only fields are ignored here
|
|
21
|
+
runtime:
|
|
22
|
+
import: my_module:MyModel # entrypoint, required
|
|
23
|
+
config: config.yaml # optional model config
|
|
24
|
+
weights_path: ./weights # optional weights root override
|
|
25
|
+
|
|
26
|
+
* Legacy (top-level keys, kept for backwards compatibility)::
|
|
27
|
+
|
|
28
|
+
model: my_module:MyModel # entrypoint
|
|
29
|
+
name: my-model # optional slug
|
|
30
|
+
config: config.yaml # optional model config
|
|
31
|
+
weights_path: ./weights # optional weights root override
|
|
32
|
+
|
|
33
|
+
Loading a legacy file emits a one-shot deprecation warning pointing
|
|
34
|
+
partners at the modern shape and the ``reactor`` Go CLI.
|
|
35
|
+
|
|
36
|
+
The runtime only reads the few fields it needs; any additional keys —
|
|
37
|
+
in particular the rich ``model.*`` metadata used by the ``reactor`` CLI
|
|
38
|
+
(``display-name``, ``description``, ``gpu``, ``extra-args``, ...) —
|
|
39
|
+
are deliberately ignored.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from dataclasses import dataclass, field
|
|
45
|
+
from typing import Any, Dict, List, Optional
|
|
46
|
+
|
|
47
|
+
from reactor_runtime.utils.log import get_logger
|
|
48
|
+
|
|
49
|
+
_logger = get_logger(__name__)
|
|
50
|
+
|
|
51
|
+
# Multi-line WARNING surfaced once per process when ``from_dict`` is
|
|
52
|
+
# invoked on a legacy (top-level ``model``/``name``/``config``) reactor.yaml.
|
|
53
|
+
# The runtime keeps reading the legacy shape so partners aren't broken,
|
|
54
|
+
# but we want them to migrate to the richer shape — either by hand or by
|
|
55
|
+
# regenerating the file with the ``reactor`` Go CLI.
|
|
56
|
+
_LEGACY_DEPRECATION_MSG = (
|
|
57
|
+
"reactor.yaml is using the legacy top-level shape "
|
|
58
|
+
"(model: / name: / config:); this is DEPRECATED and will be removed "
|
|
59
|
+
"in a future release. Please migrate to the modern shape:\n"
|
|
60
|
+
" model:\n"
|
|
61
|
+
" name: my-model\n"
|
|
62
|
+
" runtime:\n"
|
|
63
|
+
" import: my_module:MyModel\n"
|
|
64
|
+
" config: config.yaml\n"
|
|
65
|
+
"The 'reactor' Go CLI generates this format automatically; install "
|
|
66
|
+
"with `brew install reactor-team/tools/reactor-cli` and run "
|
|
67
|
+
"`reactor init <name>` in a fresh workspace, or copy the new shape "
|
|
68
|
+
"by hand."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Module-level guard so the warning fires once per process even if multiple
|
|
72
|
+
# reactor.yaml files are loaded (e.g. from tests). Reset by tests via
|
|
73
|
+
# ``_reset_legacy_warning_for_tests``.
|
|
74
|
+
_legacy_warned = False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _reset_legacy_warning_for_tests() -> None:
|
|
78
|
+
"""Test helper: clear the once-per-process warning latch."""
|
|
79
|
+
global _legacy_warned
|
|
80
|
+
_legacy_warned = False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class ReactorConfig:
|
|
85
|
+
"""Parsed ``reactor.yaml``.
|
|
86
|
+
|
|
87
|
+
Attributes:
|
|
88
|
+
model: The model entrypoint spec, ``module:ClassName``. Sourced
|
|
89
|
+
from ``runtime.import`` (modern) or top-level ``model``
|
|
90
|
+
(legacy).
|
|
91
|
+
name: Model slug used on the wire (Redis streams, logs).
|
|
92
|
+
Sourced from ``model.name`` (modern) or top-level ``name``
|
|
93
|
+
(legacy). Empty string when not set.
|
|
94
|
+
config: Optional path to a model-specific YAML config. Sourced
|
|
95
|
+
from ``runtime.config`` (modern) or top-level ``config``
|
|
96
|
+
(legacy). The caller is responsible for resolving relative
|
|
97
|
+
paths to absolute before passing to :func:`build_model`.
|
|
98
|
+
config_overrides: CLI dotlist overrides
|
|
99
|
+
(e.g. ``["lr=0.001", "batch=4"]``) merged on top of the
|
|
100
|
+
loaded config file.
|
|
101
|
+
weights_path: Optional override for the weights root consumed by
|
|
102
|
+
:func:`reactor_runtime.get_weights_path`. Sourced from
|
|
103
|
+
``runtime.weights_path`` (modern) or top-level ``weights_path``
|
|
104
|
+
(legacy). When set, takes precedence over the built-in default
|
|
105
|
+
but is itself overridden by ``$REACTOR_WEIGHTS_PATH``. The
|
|
106
|
+
runtime CLI resolves relative paths against the model root
|
|
107
|
+
before registering this with the helper, so partners can
|
|
108
|
+
commit ``runtime.weights_path: ./weights`` and have it work
|
|
109
|
+
from any cwd.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
model: str
|
|
113
|
+
name: str = ""
|
|
114
|
+
config: Optional[str] = None
|
|
115
|
+
config_overrides: List[str] = field(default_factory=list)
|
|
116
|
+
weights_path: Optional[str] = None
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def from_dict(cls, raw: Dict[str, Any]) -> "ReactorConfig":
|
|
120
|
+
"""Parse a raw YAML dict into a :class:`ReactorConfig`.
|
|
121
|
+
|
|
122
|
+
Accepts both the modern (``model:`` / ``runtime:`` sections) and
|
|
123
|
+
legacy (top-level ``model`` / ``name`` / ``config``) shapes.
|
|
124
|
+
Detection is based on whether ``runtime`` is a mapping or
|
|
125
|
+
``model`` is a mapping (legacy ``model`` is always a string).
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
KeyError: when neither schema provides a model entrypoint.
|
|
129
|
+
"""
|
|
130
|
+
runtime_section = raw.get("runtime")
|
|
131
|
+
model_section = raw.get("model")
|
|
132
|
+
|
|
133
|
+
modern = isinstance(runtime_section, dict) or isinstance(model_section, dict)
|
|
134
|
+
|
|
135
|
+
if modern:
|
|
136
|
+
runtime_section = (
|
|
137
|
+
runtime_section if isinstance(runtime_section, dict) else {}
|
|
138
|
+
)
|
|
139
|
+
model_section = model_section if isinstance(model_section, dict) else {}
|
|
140
|
+
|
|
141
|
+
entrypoint = runtime_section.get("import")
|
|
142
|
+
if not entrypoint:
|
|
143
|
+
raise KeyError(
|
|
144
|
+
"runtime.import is required in reactor.yaml "
|
|
145
|
+
"(e.g. 'runtime:\\n import: my_module:MyModel')"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
slug = model_section.get("name") or ""
|
|
149
|
+
|
|
150
|
+
return cls(
|
|
151
|
+
model=str(entrypoint),
|
|
152
|
+
name=str(slug),
|
|
153
|
+
config=runtime_section.get("config"),
|
|
154
|
+
weights_path=runtime_section.get("weights_path") or None,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if "model" not in raw:
|
|
158
|
+
raise KeyError("model")
|
|
159
|
+
|
|
160
|
+
global _legacy_warned
|
|
161
|
+
if not _legacy_warned:
|
|
162
|
+
_legacy_warned = True
|
|
163
|
+
_logger.warning(_LEGACY_DEPRECATION_MSG)
|
|
164
|
+
|
|
165
|
+
return cls(
|
|
166
|
+
model=raw["model"],
|
|
167
|
+
name=raw.get("name", "") or "",
|
|
168
|
+
config=raw.get("config"),
|
|
169
|
+
weights_path=raw.get("weights_path") or None,
|
|
170
|
+
)
|
|
@@ -54,6 +54,10 @@ from reactor_runtime.interface.internal.input_buffer import (
|
|
|
54
54
|
ReadMode,
|
|
55
55
|
)
|
|
56
56
|
|
|
57
|
+
# Inbound frame with timing metadata (re-exported from transports so model
|
|
58
|
+
# authors can ``from reactor_runtime.interface import InputFrame``).
|
|
59
|
+
from reactor_runtime.transports.media import InputFrame
|
|
60
|
+
|
|
57
61
|
__all__ = [
|
|
58
62
|
# Tracks
|
|
59
63
|
"Output",
|
|
@@ -95,4 +99,6 @@ __all__ = [
|
|
|
95
99
|
"InputBuffer",
|
|
96
100
|
"BufferClosed",
|
|
97
101
|
"ReadMode",
|
|
102
|
+
# Inbound frame
|
|
103
|
+
"InputFrame",
|
|
98
104
|
]
|
|
@@ -6,7 +6,9 @@ from __future__ import annotations
|
|
|
6
6
|
import asyncio
|
|
7
7
|
import dataclasses
|
|
8
8
|
import inspect
|
|
9
|
-
from typing import Any, Dict, Iterator, List, Optional, Type
|
|
9
|
+
from typing import Any, Dict, Iterator, List, Optional, Type, Union
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
10
12
|
|
|
11
13
|
from reactor_runtime.interface.driver.step_result import StepResult
|
|
12
14
|
from reactor_runtime.interface.events.messages import ModelMessage
|
|
@@ -17,6 +19,7 @@ from reactor_runtime.interface.pipeline.reactor_pipeline import (
|
|
|
17
19
|
GeneratorEnded,
|
|
18
20
|
ReactorPipeline,
|
|
19
21
|
)
|
|
22
|
+
from reactor_runtime.transports.media import InputFrame
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class PipelineExecutor(Iterator[StepResult]):
|
|
@@ -166,14 +169,29 @@ class PipelineExecutor(Iterator[StepResult]):
|
|
|
166
169
|
}
|
|
167
170
|
return self._run(self._send_event(entry, **handler_kwargs))
|
|
168
171
|
|
|
169
|
-
def push_media(self, track_name: str, data:
|
|
172
|
+
def push_media(self, track_name: str, data: Union[np.ndarray, InputFrame]) -> None:
|
|
170
173
|
"""Push a media frame into an input buffer.
|
|
171
174
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
``
|
|
175
|
+
Accepts either a raw ``np.ndarray`` (auto-wrapped into an
|
|
176
|
+
:class:`~reactor_runtime.transports.media.InputFrame` with
|
|
177
|
+
``pts=None``) or a prebuilt :class:`InputFrame` for callers
|
|
178
|
+
that want to stamp the frame with an explicit presentation
|
|
179
|
+
timestamp.
|
|
180
|
+
|
|
181
|
+
The frame becomes available to the model on the next iteration
|
|
182
|
+
when ``inference()`` calls ``try_read()`` / ``read()`` — both
|
|
183
|
+
of which now return ``List[InputFrame]``.
|
|
175
184
|
"""
|
|
176
|
-
|
|
185
|
+
if isinstance(data, InputFrame):
|
|
186
|
+
frame = data
|
|
187
|
+
elif isinstance(data, np.ndarray):
|
|
188
|
+
frame = InputFrame(data=data)
|
|
189
|
+
else:
|
|
190
|
+
raise TypeError(
|
|
191
|
+
"push_media expects an np.ndarray or InputFrame, got "
|
|
192
|
+
f"{type(data).__name__}"
|
|
193
|
+
)
|
|
194
|
+
self._model._push_media(track_name, frame)
|
|
177
195
|
|
|
178
196
|
def disconnect(self) -> List[ModelMessage]:
|
|
179
197
|
"""End the current session.
|
{reactor_runtime-2.3.2 → reactor_runtime-2.5.0}/src/reactor_runtime/interface/events/messages.py
RENAMED
|
@@ -93,10 +93,20 @@ class ModelMessage:
|
|
|
93
93
|
|
|
94
94
|
name: ClassVar[str]
|
|
95
95
|
_field_descriptions: ClassVar[Dict[str, str]]
|
|
96
|
+
_user_doc: ClassVar[Optional[str]]
|
|
96
97
|
|
|
97
98
|
def __init_subclass__(cls, **kwargs: object) -> None:
|
|
98
99
|
super().__init_subclass__(**kwargs)
|
|
99
100
|
|
|
101
|
+
# Snapshot the user-supplied docstring before any `@dataclass`
|
|
102
|
+
# decoration runs. ``dataclasses.dataclass`` rewrites
|
|
103
|
+
# ``__doc__`` to an auto-generated signature like
|
|
104
|
+
# ``"Foo(x: int)"`` whenever the class body has no explicit
|
|
105
|
+
# docstring, which would leak meaningless noise into the
|
|
106
|
+
# schema description. Reading ``cls.__dict__`` (not
|
|
107
|
+
# ``cls.__doc__``) avoids inheriting the parent's docstring.
|
|
108
|
+
cls._user_doc = cls.__dict__.get("__doc__", None)
|
|
109
|
+
|
|
100
110
|
descriptions: Dict[str, str] = {}
|
|
101
111
|
has_default: list[str] = []
|
|
102
112
|
no_default: list[str] = []
|