reactor-sdk 0.1.0__py3-none-any.whl
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/__init__.py +57 -0
- reactor_sdk/coordinator/__init__.py +13 -0
- reactor_sdk/coordinator/client.py +362 -0
- reactor_sdk/coordinator/local_client.py +163 -0
- reactor_sdk/interface.py +203 -0
- reactor_sdk/model/__init__.py +11 -0
- reactor_sdk/model/client.py +647 -0
- reactor_sdk/py.typed +2 -0
- reactor_sdk/reactor.py +739 -0
- reactor_sdk/types.py +255 -0
- reactor_sdk/utils/__init__.py +25 -0
- reactor_sdk/utils/tokens.py +64 -0
- reactor_sdk/utils/webrtc.py +315 -0
- reactor_sdk-0.1.0.dist-info/METADATA +204 -0
- reactor_sdk-0.1.0.dist-info/RECORD +17 -0
- reactor_sdk-0.1.0.dist-info/WHEEL +4 -0
- reactor_sdk-0.1.0.dist-info/licenses/LICENSE +21 -0
reactor_sdk/interface.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reactor - Public API for the Reactor SDK.
|
|
3
|
+
|
|
4
|
+
This module defines the Reactor class that users interact with.
|
|
5
|
+
Internal implementation details are hidden in the reactor module.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
|
|
11
|
+
|
|
12
|
+
from aiortc import MediaStreamTrack
|
|
13
|
+
|
|
14
|
+
from reactor_sdk.types import (
|
|
15
|
+
FrameCallback,
|
|
16
|
+
ReactorError,
|
|
17
|
+
ReactorEvent,
|
|
18
|
+
ReactorState,
|
|
19
|
+
ReactorStatus,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Type variable for decorator return types
|
|
23
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
24
|
+
|
|
25
|
+
# Type for event handlers
|
|
26
|
+
EventHandler = Callable[..., None]
|
|
27
|
+
|
|
28
|
+
# Default coordinator URL
|
|
29
|
+
PROD_COORDINATOR_URL = "https://api.reactor.inc"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Reactor:
|
|
33
|
+
"""
|
|
34
|
+
Main entry point for the Reactor SDK.
|
|
35
|
+
|
|
36
|
+
Provides real-time AI video streaming via WebRTC.
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
from reactor_sdk import Reactor, ReactorStatus
|
|
40
|
+
|
|
41
|
+
reactor = Reactor(model_name="my-model", api_key="your-api-key")
|
|
42
|
+
|
|
43
|
+
@reactor.on_frame
|
|
44
|
+
def handle_frame(frame):
|
|
45
|
+
print(f"Frame shape: {frame.shape}")
|
|
46
|
+
|
|
47
|
+
@reactor.on_status(ReactorStatus.READY)
|
|
48
|
+
def handle_ready(status):
|
|
49
|
+
print("Connected!")
|
|
50
|
+
|
|
51
|
+
await reactor.connect()
|
|
52
|
+
await reactor.send_command("setParameter", {"value": 0.5})
|
|
53
|
+
await reactor.disconnect()
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __new__(
|
|
57
|
+
cls,
|
|
58
|
+
model_name: str,
|
|
59
|
+
api_key: Optional[str] = None,
|
|
60
|
+
coordinator_url: str = PROD_COORDINATOR_URL,
|
|
61
|
+
local: bool = False,
|
|
62
|
+
) -> "Reactor":
|
|
63
|
+
"""
|
|
64
|
+
Create a new Reactor instance.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
model_name: Name of the model to connect to.
|
|
68
|
+
api_key: Your Reactor API key. The SDK will automatically fetch
|
|
69
|
+
a JWT token using this key. Required unless local=True.
|
|
70
|
+
coordinator_url: URL of the coordinator API (ignored if local=True).
|
|
71
|
+
local: If True, use local coordinator at localhost:8080.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
A Reactor instance ready to be connected.
|
|
75
|
+
"""
|
|
76
|
+
# Import here to avoid circular imports
|
|
77
|
+
from reactor_sdk.reactor import _ReactorImpl
|
|
78
|
+
|
|
79
|
+
return _ReactorImpl( # type: ignore[return-value]
|
|
80
|
+
model_name=model_name,
|
|
81
|
+
api_key=api_key,
|
|
82
|
+
coordinator_url=coordinator_url,
|
|
83
|
+
local=local,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# =========================================================================
|
|
87
|
+
# Event Registration
|
|
88
|
+
# =========================================================================
|
|
89
|
+
|
|
90
|
+
def on(self, event: ReactorEvent, handler: EventHandler) -> None:
|
|
91
|
+
"""Register an event handler."""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
def off(self, event: ReactorEvent, handler: EventHandler) -> None:
|
|
95
|
+
"""Unregister an event handler."""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
# =========================================================================
|
|
99
|
+
# Decorators
|
|
100
|
+
# =========================================================================
|
|
101
|
+
|
|
102
|
+
def on_frame(self, func: F) -> F:
|
|
103
|
+
"""Decorator to register a frame callback."""
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
def on_message(self, func: F) -> F:
|
|
107
|
+
"""Decorator to register a message handler."""
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
def on_status(
|
|
111
|
+
self,
|
|
112
|
+
func_or_filter: Union[F, ReactorStatus, list[ReactorStatus], None] = None,
|
|
113
|
+
) -> Union[F, Callable[[F], F]]:
|
|
114
|
+
"""Decorator to register a status change handler (with optional filter)."""
|
|
115
|
+
...
|
|
116
|
+
|
|
117
|
+
def on_error(self, func: F) -> F:
|
|
118
|
+
"""Decorator to register an error handler."""
|
|
119
|
+
...
|
|
120
|
+
|
|
121
|
+
def on_stream(self, func: F) -> F:
|
|
122
|
+
"""Decorator to register a stream/track handler."""
|
|
123
|
+
...
|
|
124
|
+
|
|
125
|
+
# =========================================================================
|
|
126
|
+
# Connection
|
|
127
|
+
# =========================================================================
|
|
128
|
+
|
|
129
|
+
async def connect(self) -> None:
|
|
130
|
+
"""Connect to the coordinator and model."""
|
|
131
|
+
...
|
|
132
|
+
|
|
133
|
+
async def reconnect(self) -> None:
|
|
134
|
+
"""Reconnect to an existing session."""
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
async def disconnect(self, recoverable: bool = False) -> None:
|
|
138
|
+
"""Disconnect from the coordinator and model."""
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
# =========================================================================
|
|
142
|
+
# Communication
|
|
143
|
+
# =========================================================================
|
|
144
|
+
|
|
145
|
+
async def send_command(self, command: str, data: Any) -> None:
|
|
146
|
+
"""Send a command to the model."""
|
|
147
|
+
...
|
|
148
|
+
|
|
149
|
+
# =========================================================================
|
|
150
|
+
# Track Publishing
|
|
151
|
+
# =========================================================================
|
|
152
|
+
|
|
153
|
+
async def publish_track(self, track: MediaStreamTrack) -> None:
|
|
154
|
+
"""Publish a video track to the model."""
|
|
155
|
+
...
|
|
156
|
+
|
|
157
|
+
async def unpublish_track(self) -> None:
|
|
158
|
+
"""Unpublish the currently published track."""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
# =========================================================================
|
|
162
|
+
# Frame Callback
|
|
163
|
+
# =========================================================================
|
|
164
|
+
|
|
165
|
+
def set_frame_callback(self, callback: Optional[FrameCallback]) -> None:
|
|
166
|
+
"""Set a callback to receive individual video frames."""
|
|
167
|
+
...
|
|
168
|
+
|
|
169
|
+
# =========================================================================
|
|
170
|
+
# State Accessors
|
|
171
|
+
# =========================================================================
|
|
172
|
+
|
|
173
|
+
def get_remote_track(self) -> Optional[MediaStreamTrack]:
|
|
174
|
+
"""Get the remote video track from the model."""
|
|
175
|
+
...
|
|
176
|
+
|
|
177
|
+
def get_status(self) -> ReactorStatus:
|
|
178
|
+
"""Get the current connection status."""
|
|
179
|
+
...
|
|
180
|
+
|
|
181
|
+
def get_state(self) -> ReactorState:
|
|
182
|
+
"""Get the current state including status and error info."""
|
|
183
|
+
...
|
|
184
|
+
|
|
185
|
+
def get_session_id(self) -> Optional[str]:
|
|
186
|
+
"""Get the current session ID."""
|
|
187
|
+
...
|
|
188
|
+
|
|
189
|
+
def get_last_error(self) -> Optional[ReactorError]:
|
|
190
|
+
"""Get the last error that occurred."""
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
# =========================================================================
|
|
194
|
+
# Context Manager
|
|
195
|
+
# =========================================================================
|
|
196
|
+
|
|
197
|
+
async def __aenter__(self) -> "Reactor":
|
|
198
|
+
"""Async context manager entry."""
|
|
199
|
+
...
|
|
200
|
+
|
|
201
|
+
async def __aexit__(self, *args: object) -> None:
|
|
202
|
+
"""Async context manager exit."""
|
|
203
|
+
...
|