reactor-sdk 0.1.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_sdk-0.1.0/.github/workflows/ci.yml +26 -0
- reactor_sdk-0.1.0/.github/workflows/publish.yml +31 -0
- reactor_sdk-0.1.0/.gitignore +131 -0
- reactor_sdk-0.1.0/LICENSE +21 -0
- reactor_sdk-0.1.0/PKG-INFO +204 -0
- reactor_sdk-0.1.0/README.md +174 -0
- reactor_sdk-0.1.0/examples/pygame_app/README.md +126 -0
- reactor_sdk-0.1.0/examples/pygame_app/controller.py +786 -0
- reactor_sdk-0.1.0/examples/pygame_app/main.py +427 -0
- reactor_sdk-0.1.0/examples/pygame_app/requirements.txt +6 -0
- reactor_sdk-0.1.0/examples/rtmp_app/README.md +104 -0
- reactor_sdk-0.1.0/examples/rtmp_app/main.py +237 -0
- reactor_sdk-0.1.0/examples/rtmp_app/requirements.txt +13 -0
- reactor_sdk-0.1.0/pyproject.toml +64 -0
- reactor_sdk-0.1.0/src/reactor_sdk/__init__.py +57 -0
- reactor_sdk-0.1.0/src/reactor_sdk/coordinator/__init__.py +13 -0
- reactor_sdk-0.1.0/src/reactor_sdk/coordinator/client.py +362 -0
- reactor_sdk-0.1.0/src/reactor_sdk/coordinator/local_client.py +163 -0
- reactor_sdk-0.1.0/src/reactor_sdk/interface.py +203 -0
- reactor_sdk-0.1.0/src/reactor_sdk/model/__init__.py +11 -0
- reactor_sdk-0.1.0/src/reactor_sdk/model/client.py +647 -0
- reactor_sdk-0.1.0/src/reactor_sdk/py.typed +2 -0
- reactor_sdk-0.1.0/src/reactor_sdk/reactor.py +739 -0
- reactor_sdk-0.1.0/src/reactor_sdk/types.py +255 -0
- reactor_sdk-0.1.0/src/reactor_sdk/utils/__init__.py +25 -0
- reactor_sdk-0.1.0/src/reactor_sdk/utils/tokens.py +64 -0
- reactor_sdk-0.1.0/src/reactor_sdk/utils/webrtc.py +315 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip
|
|
23
|
+
pip install build
|
|
24
|
+
|
|
25
|
+
- name: Build package
|
|
26
|
+
run: python -m build
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: pypi
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Build package
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install build
|
|
28
|
+
python -m build
|
|
29
|
+
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
*.manifest
|
|
29
|
+
*.spec
|
|
30
|
+
|
|
31
|
+
# Installer logs
|
|
32
|
+
pip-log.txt
|
|
33
|
+
pip-delete-this-directory.txt
|
|
34
|
+
|
|
35
|
+
# Unit test / coverage reports
|
|
36
|
+
htmlcov/
|
|
37
|
+
.tox/
|
|
38
|
+
.nox/
|
|
39
|
+
.coverage
|
|
40
|
+
.coverage.*
|
|
41
|
+
.cache
|
|
42
|
+
nosetests.xml
|
|
43
|
+
coverage.xml
|
|
44
|
+
*.cover
|
|
45
|
+
*.py,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
pytest_cache/
|
|
49
|
+
|
|
50
|
+
# Translations
|
|
51
|
+
*.mo
|
|
52
|
+
*.pot
|
|
53
|
+
|
|
54
|
+
# Django stuff:
|
|
55
|
+
*.log
|
|
56
|
+
local_settings.py
|
|
57
|
+
|
|
58
|
+
# Flask stuff:
|
|
59
|
+
instance/
|
|
60
|
+
.webassets-cache
|
|
61
|
+
|
|
62
|
+
# Scrapy stuff:
|
|
63
|
+
.scrapy
|
|
64
|
+
|
|
65
|
+
# Sphinx documentation
|
|
66
|
+
docs/_build/
|
|
67
|
+
|
|
68
|
+
# PyBuilder
|
|
69
|
+
target/
|
|
70
|
+
|
|
71
|
+
# Jupyter Notebook
|
|
72
|
+
.ipynb_checkpoints
|
|
73
|
+
|
|
74
|
+
# IPython
|
|
75
|
+
profile_default/
|
|
76
|
+
ipython_config.py
|
|
77
|
+
|
|
78
|
+
# pyenv
|
|
79
|
+
.python-version
|
|
80
|
+
|
|
81
|
+
# celery beat schedule file
|
|
82
|
+
celerybeat-schedule
|
|
83
|
+
|
|
84
|
+
# SageMath parsed files
|
|
85
|
+
*.sage.py
|
|
86
|
+
|
|
87
|
+
# Environments
|
|
88
|
+
.env
|
|
89
|
+
.venv
|
|
90
|
+
env/
|
|
91
|
+
venv/
|
|
92
|
+
ENV/
|
|
93
|
+
env.bak/
|
|
94
|
+
venv.bak/
|
|
95
|
+
|
|
96
|
+
# Spyder project settings
|
|
97
|
+
.spyderproject
|
|
98
|
+
.spyproject
|
|
99
|
+
|
|
100
|
+
# Rope project settings
|
|
101
|
+
.ropeproject
|
|
102
|
+
|
|
103
|
+
# mkdocs documentation
|
|
104
|
+
/site
|
|
105
|
+
|
|
106
|
+
# mypy
|
|
107
|
+
.mypy_cache/
|
|
108
|
+
.dmypy.json
|
|
109
|
+
dmypy.json
|
|
110
|
+
|
|
111
|
+
# Pyre type checker
|
|
112
|
+
.pyre/
|
|
113
|
+
|
|
114
|
+
# pytype static type analyzer
|
|
115
|
+
.pytype/
|
|
116
|
+
|
|
117
|
+
# Cython debug symbols
|
|
118
|
+
cython_debug/
|
|
119
|
+
|
|
120
|
+
# IDE
|
|
121
|
+
.idea/
|
|
122
|
+
.vscode/
|
|
123
|
+
*.swp
|
|
124
|
+
*.swo
|
|
125
|
+
|
|
126
|
+
# OS
|
|
127
|
+
.DS_Store
|
|
128
|
+
Thumbs.db
|
|
129
|
+
|
|
130
|
+
# Project specific
|
|
131
|
+
*.log
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Reactor Technologies, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reactor-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Reactor - Real-time AI video streaming
|
|
5
|
+
Project-URL: Homepage, https://reactor.inc
|
|
6
|
+
Project-URL: Documentation, https://docs.reactor.inc
|
|
7
|
+
Project-URL: Repository, https://github.com/reactor-team/py-sdk
|
|
8
|
+
Author-email: Reactor Technologies <support@reactor.inc>
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Multimedia :: Video
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
21
|
+
Requires-Dist: aiortc>=1.9.0
|
|
22
|
+
Requires-Dist: av>=12.0.0
|
|
23
|
+
Requires-Dist: numpy>=1.24.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.3.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Reactor Python SDK
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/reactor-sdk/)
|
|
34
|
+
[](https://pypi.org/project/reactor-sdk/)
|
|
35
|
+
[](https://github.com/reactor-team/py-sdk/actions)
|
|
36
|
+
[](./LICENSE)
|
|
37
|
+
|
|
38
|
+
Python SDK for Reactor - Real-time AI video streaming platform.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install reactor-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import asyncio
|
|
50
|
+
from reactor_sdk import Reactor, ReactorStatus
|
|
51
|
+
|
|
52
|
+
async def main():
|
|
53
|
+
# Create a Reactor instance with your API key
|
|
54
|
+
reactor = Reactor(
|
|
55
|
+
model_name="my-model",
|
|
56
|
+
api_key="REACTOR_API_KEY", # SDK automatically fetches JWT token
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Use decorators for clean event handling
|
|
60
|
+
@reactor.on_frame
|
|
61
|
+
def handle_frame(frame):
|
|
62
|
+
print(f"Received frame: {frame.shape}")
|
|
63
|
+
|
|
64
|
+
@reactor.on_message
|
|
65
|
+
def handle_message(msg):
|
|
66
|
+
print(f"Message: {msg}")
|
|
67
|
+
|
|
68
|
+
@reactor.on_status(ReactorStatus.READY)
|
|
69
|
+
def handle_ready(status):
|
|
70
|
+
print("Connected and ready!")
|
|
71
|
+
|
|
72
|
+
@reactor.on_error
|
|
73
|
+
def handle_error(error):
|
|
74
|
+
print(f"Error: {error}")
|
|
75
|
+
|
|
76
|
+
# Connect to the model (JWT token is fetched automatically)
|
|
77
|
+
await reactor.connect()
|
|
78
|
+
|
|
79
|
+
# Send commands
|
|
80
|
+
await reactor.send_command("setParameter", {"value": 0.5})
|
|
81
|
+
|
|
82
|
+
# Keep running
|
|
83
|
+
try:
|
|
84
|
+
while reactor.get_status() == ReactorStatus.READY:
|
|
85
|
+
await asyncio.sleep(0.1)
|
|
86
|
+
finally:
|
|
87
|
+
await reactor.disconnect()
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
asyncio.run(main())
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Features
|
|
94
|
+
|
|
95
|
+
- **WebRTC video streaming** via aiortc
|
|
96
|
+
- **Event-driven API** matching the JavaScript SDK
|
|
97
|
+
- **Frame callbacks** for single-frame access
|
|
98
|
+
- **Video input** support for sending video to models
|
|
99
|
+
- **Local development** mode for testing
|
|
100
|
+
- **Full type hints** for IDE support
|
|
101
|
+
|
|
102
|
+
## API Reference
|
|
103
|
+
|
|
104
|
+
### Reactor
|
|
105
|
+
|
|
106
|
+
The main class for connecting to Reactor models.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from reactor_sdk import Reactor
|
|
110
|
+
|
|
111
|
+
# Production usage with API key
|
|
112
|
+
reactor = Reactor(
|
|
113
|
+
model_name="my-model",
|
|
114
|
+
api_key="REACTOR_API_KEY", # SDK fetches JWT automatically
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Local development (no API key needed)
|
|
118
|
+
reactor = Reactor(
|
|
119
|
+
model_name="my-model",
|
|
120
|
+
local=True,
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The `Reactor` type can also be used for type annotations:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from reactor_sdk import Reactor
|
|
128
|
+
|
|
129
|
+
def process_reactor(reactor: Reactor) -> None:
|
|
130
|
+
# reactor has full type hints for all methods
|
|
131
|
+
pass
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Methods
|
|
135
|
+
|
|
136
|
+
- `await reactor.connect()` - Connect to the model (fetches JWT automatically if API key provided)
|
|
137
|
+
- `await reactor.disconnect(recoverable: bool = False)` - Disconnect
|
|
138
|
+
- `await reactor.reconnect()` - Reconnect to existing session
|
|
139
|
+
- `await reactor.send_command(command: str, data: dict)` - Send a command
|
|
140
|
+
- `await reactor.publish_track(track: MediaStreamTrack)` - Send video to model
|
|
141
|
+
- `await reactor.unpublish_track()` - Stop sending video
|
|
142
|
+
- `reactor.get_status()` - Get current status
|
|
143
|
+
- `reactor.get_session_id()` - Get session ID
|
|
144
|
+
- `reactor.set_frame_callback(callback)` - Set frame callback
|
|
145
|
+
|
|
146
|
+
#### Decorators
|
|
147
|
+
|
|
148
|
+
Use decorators for clean event handling:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
@reactor.on_frame
|
|
152
|
+
def handle_frame(frame):
|
|
153
|
+
"""Called for each video frame (numpy array H,W,3)."""
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
@reactor.on_message
|
|
157
|
+
def handle_message(message):
|
|
158
|
+
"""Called for each message from the model."""
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
@reactor.on_status
|
|
162
|
+
def handle_any_status(status):
|
|
163
|
+
"""Called for all status changes."""
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
@reactor.on_status(ReactorStatus.READY)
|
|
167
|
+
def handle_ready(status):
|
|
168
|
+
"""Called only when status becomes READY."""
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
@reactor.on_status([ReactorStatus.READY, ReactorStatus.CONNECTING])
|
|
172
|
+
def handle_active(status):
|
|
173
|
+
"""Called when status is READY or CONNECTING."""
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
@reactor.on_error
|
|
177
|
+
def handle_error(error):
|
|
178
|
+
"""Called when an error occurs."""
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
@reactor.on_stream
|
|
182
|
+
def handle_stream(track):
|
|
183
|
+
"""Called when video stream/track changes."""
|
|
184
|
+
pass
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Events (alternative to decorators)
|
|
188
|
+
|
|
189
|
+
- `status_changed` - Status changed (disconnected, connecting, ready)
|
|
190
|
+
- `session_id_changed` - Session ID changed
|
|
191
|
+
- `new_message` - Message received from model
|
|
192
|
+
- `stream_changed` - Video stream changed
|
|
193
|
+
- `error` - Error occurred
|
|
194
|
+
|
|
195
|
+
## Examples
|
|
196
|
+
|
|
197
|
+
See the `examples/` directory for complete examples:
|
|
198
|
+
|
|
199
|
+
- `pygame_app/` - Pygame application with dynamic UI controls
|
|
200
|
+
- `rtmp_app/` - Stream Reactor video to RTMP servers (Twitch, YouTube, etc.)
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT License - Copyright (c) 2025 Reactor Technologies, Inc.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Reactor Python SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/reactor-sdk/)
|
|
4
|
+
[](https://pypi.org/project/reactor-sdk/)
|
|
5
|
+
[](https://github.com/reactor-team/py-sdk/actions)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
Python SDK for Reactor - Real-time AI video streaming platform.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install reactor-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import asyncio
|
|
20
|
+
from reactor_sdk import Reactor, ReactorStatus
|
|
21
|
+
|
|
22
|
+
async def main():
|
|
23
|
+
# Create a Reactor instance with your API key
|
|
24
|
+
reactor = Reactor(
|
|
25
|
+
model_name="my-model",
|
|
26
|
+
api_key="REACTOR_API_KEY", # SDK automatically fetches JWT token
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Use decorators for clean event handling
|
|
30
|
+
@reactor.on_frame
|
|
31
|
+
def handle_frame(frame):
|
|
32
|
+
print(f"Received frame: {frame.shape}")
|
|
33
|
+
|
|
34
|
+
@reactor.on_message
|
|
35
|
+
def handle_message(msg):
|
|
36
|
+
print(f"Message: {msg}")
|
|
37
|
+
|
|
38
|
+
@reactor.on_status(ReactorStatus.READY)
|
|
39
|
+
def handle_ready(status):
|
|
40
|
+
print("Connected and ready!")
|
|
41
|
+
|
|
42
|
+
@reactor.on_error
|
|
43
|
+
def handle_error(error):
|
|
44
|
+
print(f"Error: {error}")
|
|
45
|
+
|
|
46
|
+
# Connect to the model (JWT token is fetched automatically)
|
|
47
|
+
await reactor.connect()
|
|
48
|
+
|
|
49
|
+
# Send commands
|
|
50
|
+
await reactor.send_command("setParameter", {"value": 0.5})
|
|
51
|
+
|
|
52
|
+
# Keep running
|
|
53
|
+
try:
|
|
54
|
+
while reactor.get_status() == ReactorStatus.READY:
|
|
55
|
+
await asyncio.sleep(0.1)
|
|
56
|
+
finally:
|
|
57
|
+
await reactor.disconnect()
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
asyncio.run(main())
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
- **WebRTC video streaming** via aiortc
|
|
66
|
+
- **Event-driven API** matching the JavaScript SDK
|
|
67
|
+
- **Frame callbacks** for single-frame access
|
|
68
|
+
- **Video input** support for sending video to models
|
|
69
|
+
- **Local development** mode for testing
|
|
70
|
+
- **Full type hints** for IDE support
|
|
71
|
+
|
|
72
|
+
## API Reference
|
|
73
|
+
|
|
74
|
+
### Reactor
|
|
75
|
+
|
|
76
|
+
The main class for connecting to Reactor models.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from reactor_sdk import Reactor
|
|
80
|
+
|
|
81
|
+
# Production usage with API key
|
|
82
|
+
reactor = Reactor(
|
|
83
|
+
model_name="my-model",
|
|
84
|
+
api_key="REACTOR_API_KEY", # SDK fetches JWT automatically
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Local development (no API key needed)
|
|
88
|
+
reactor = Reactor(
|
|
89
|
+
model_name="my-model",
|
|
90
|
+
local=True,
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The `Reactor` type can also be used for type annotations:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from reactor_sdk import Reactor
|
|
98
|
+
|
|
99
|
+
def process_reactor(reactor: Reactor) -> None:
|
|
100
|
+
# reactor has full type hints for all methods
|
|
101
|
+
pass
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Methods
|
|
105
|
+
|
|
106
|
+
- `await reactor.connect()` - Connect to the model (fetches JWT automatically if API key provided)
|
|
107
|
+
- `await reactor.disconnect(recoverable: bool = False)` - Disconnect
|
|
108
|
+
- `await reactor.reconnect()` - Reconnect to existing session
|
|
109
|
+
- `await reactor.send_command(command: str, data: dict)` - Send a command
|
|
110
|
+
- `await reactor.publish_track(track: MediaStreamTrack)` - Send video to model
|
|
111
|
+
- `await reactor.unpublish_track()` - Stop sending video
|
|
112
|
+
- `reactor.get_status()` - Get current status
|
|
113
|
+
- `reactor.get_session_id()` - Get session ID
|
|
114
|
+
- `reactor.set_frame_callback(callback)` - Set frame callback
|
|
115
|
+
|
|
116
|
+
#### Decorators
|
|
117
|
+
|
|
118
|
+
Use decorators for clean event handling:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
@reactor.on_frame
|
|
122
|
+
def handle_frame(frame):
|
|
123
|
+
"""Called for each video frame (numpy array H,W,3)."""
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
@reactor.on_message
|
|
127
|
+
def handle_message(message):
|
|
128
|
+
"""Called for each message from the model."""
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
@reactor.on_status
|
|
132
|
+
def handle_any_status(status):
|
|
133
|
+
"""Called for all status changes."""
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
@reactor.on_status(ReactorStatus.READY)
|
|
137
|
+
def handle_ready(status):
|
|
138
|
+
"""Called only when status becomes READY."""
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
@reactor.on_status([ReactorStatus.READY, ReactorStatus.CONNECTING])
|
|
142
|
+
def handle_active(status):
|
|
143
|
+
"""Called when status is READY or CONNECTING."""
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
@reactor.on_error
|
|
147
|
+
def handle_error(error):
|
|
148
|
+
"""Called when an error occurs."""
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
@reactor.on_stream
|
|
152
|
+
def handle_stream(track):
|
|
153
|
+
"""Called when video stream/track changes."""
|
|
154
|
+
pass
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Events (alternative to decorators)
|
|
158
|
+
|
|
159
|
+
- `status_changed` - Status changed (disconnected, connecting, ready)
|
|
160
|
+
- `session_id_changed` - Session ID changed
|
|
161
|
+
- `new_message` - Message received from model
|
|
162
|
+
- `stream_changed` - Video stream changed
|
|
163
|
+
- `error` - Error occurred
|
|
164
|
+
|
|
165
|
+
## Examples
|
|
166
|
+
|
|
167
|
+
See the `examples/` directory for complete examples:
|
|
168
|
+
|
|
169
|
+
- `pygame_app/` - Pygame application with dynamic UI controls
|
|
170
|
+
- `rtmp_app/` - Stream Reactor video to RTMP servers (Twitch, YouTube, etc.)
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT License - Copyright (c) 2025 Reactor Technologies, Inc.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Pygame Example Application
|
|
2
|
+
|
|
3
|
+
A pygame-based application demonstrating the Reactor Python SDK with real-time video streaming and dynamic UI controls.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Real-time video stream display from Reactor models
|
|
8
|
+
- Dynamic controller UI that builds controls based on model capabilities
|
|
9
|
+
- Support for sliders, checkboxes, dropdowns, and text inputs
|
|
10
|
+
- Automatic command execution when adjusting slider values
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- Python 3.10 or higher
|
|
15
|
+
- A Reactor API key (get one at https://reactor.inc)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
1. Install the Reactor SDK (from the parent directory):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd /path/to/py-sdk
|
|
23
|
+
pip install -e .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. Install pygame:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install pygame
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or install all dependencies:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd examples/pygame_app
|
|
36
|
+
pip install -r requirements.txt
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Local Development
|
|
42
|
+
|
|
43
|
+
Connect to a local Reactor model running at `localhost:8080`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python main.py --local
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Remote Connection
|
|
50
|
+
|
|
51
|
+
Connect to a remote Reactor model using your API key:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python main.py --api-key REACTOR_API_KEY --model your-model-name
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Command Line Options
|
|
58
|
+
|
|
59
|
+
| Option | Short | Description |
|
|
60
|
+
|--------|-------|-------------|
|
|
61
|
+
| `--api-key` | `-k` | Reactor API key for authentication |
|
|
62
|
+
| `--model` | `-m` | Model name to connect to (default: `example-model`) |
|
|
63
|
+
| `--local` | `-l` | Connect to local coordinator at `localhost:8080` |
|
|
64
|
+
| `--coordinator-url` | `-c` | Custom coordinator URL |
|
|
65
|
+
| `--verbose` | `-v` | Enable verbose logging |
|
|
66
|
+
|
|
67
|
+
### Examples
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Local development
|
|
71
|
+
python main.py --local --model my-local-model
|
|
72
|
+
|
|
73
|
+
# Production with API key
|
|
74
|
+
python main.py -k sk_live_xxxxx -m production-model
|
|
75
|
+
|
|
76
|
+
# With verbose logging
|
|
77
|
+
python main.py --local -v
|
|
78
|
+
|
|
79
|
+
# Custom coordinator
|
|
80
|
+
python main.py -k REACTOR_API_KEY -m my-model -c https://custom-coordinator.example.com
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Controls
|
|
84
|
+
|
|
85
|
+
- **ESC**: Quit the application
|
|
86
|
+
- **Mouse Click**: Interact with controller UI elements
|
|
87
|
+
- **Mouse Scroll**: Scroll the controller panel
|
|
88
|
+
|
|
89
|
+
## UI Layout
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
┌─────────────────────────────────┬──────────────────┐
|
|
93
|
+
│ │ │
|
|
94
|
+
│ │ Reactor │
|
|
95
|
+
│ Video Stream │ Commands │
|
|
96
|
+
│ (960 x 720) │ │
|
|
97
|
+
│ │ [Dynamic UI │
|
|
98
|
+
│ │ controls │
|
|
99
|
+
│ │ based on │
|
|
100
|
+
│ │ model caps] │
|
|
101
|
+
│ │ │
|
|
102
|
+
└─────────────────────────────────┴──────────────────┘
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## How It Works
|
|
106
|
+
|
|
107
|
+
1. **Connection**: The app connects to the Reactor coordinator, which assigns a GPU machine
|
|
108
|
+
2. **Capabilities**: Once connected, it requests the model's capabilities schema
|
|
109
|
+
3. **Dynamic UI**: The controller parses the schema and builds appropriate UI controls
|
|
110
|
+
4. **Commands**: User interactions trigger commands sent to the model via WebRTC data channel
|
|
111
|
+
5. **Video**: The model's video output is streamed back and displayed in real-time
|
|
112
|
+
|
|
113
|
+
## Troubleshooting
|
|
114
|
+
|
|
115
|
+
### "No API key provided" error
|
|
116
|
+
Make sure to provide either `--api-key` or `--local` flag.
|
|
117
|
+
|
|
118
|
+
### Video not displaying
|
|
119
|
+
- Check that the model is running and producing output
|
|
120
|
+
- Verify the connection status indicator (green = connected)
|
|
121
|
+
- Enable verbose logging with `-v` to see detailed connection info
|
|
122
|
+
|
|
123
|
+
### Controller not showing commands
|
|
124
|
+
- Wait a few seconds after connection for capabilities to be received
|
|
125
|
+
- The app automatically retries requesting capabilities every 5 seconds
|
|
126
|
+
- Check verbose logs for any errors in the capabilities response
|