restless-stream 0.1.1__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.
- restless_stream-0.1.1/.coverage +0 -0
- restless_stream-0.1.1/.gitignore +16 -0
- restless_stream-0.1.1/PKG-INFO +412 -0
- restless_stream-0.1.1/README.md +377 -0
- restless_stream-0.1.1/pyproject.toml +76 -0
- restless_stream-0.1.1/src/restless_stream/__init__.py +65 -0
- restless_stream-0.1.1/src/restless_stream/_async_client.py +145 -0
- restless_stream-0.1.1/src/restless_stream/_client.py +7 -0
- restless_stream-0.1.1/src/restless_stream/_constants.py +1 -0
- restless_stream-0.1.1/src/restless_stream/_errors.py +18 -0
- restless_stream-0.1.1/src/restless_stream/_hmac.py +28 -0
- restless_stream-0.1.1/src/restless_stream/_models.py +196 -0
- restless_stream-0.1.1/src/restless_stream/_resources.py +133 -0
- restless_stream-0.1.1/src/restless_stream/_streaming.py +179 -0
- restless_stream-0.1.1/src/restless_stream/_sync_client.py +132 -0
- restless_stream-0.1.1/src/restless_stream/_urls.py +84 -0
- restless_stream-0.1.1/src/restless_stream/_utils.py +89 -0
- restless_stream-0.1.1/src/restless_stream/py.typed +0 -0
- restless_stream-0.1.1/tests/test_client.py +421 -0
- restless_stream-0.1.1/tests/test_hmac.py +10 -0
- restless_stream-0.1.1/tests/test_live.py +104 -0
- restless_stream-0.1.1/tests/test_streaming.py +311 -0
- restless_stream-0.1.1/uv.lock +402 -0
|
Binary file
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: restless-stream
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Python SDK for Restless Stream
|
|
5
|
+
Project-URL: Homepage, https://github.com/restless-stream/sdk/tree/main/packages/python/core#readme
|
|
6
|
+
Project-URL: Repository, https://github.com/restless-stream/sdk
|
|
7
|
+
Project-URL: Issues, https://github.com/restless-stream/sdk/issues
|
|
8
|
+
Author: Restless Stream
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: restless-stream,sdk,sse,streaming,websocket
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.14
|
|
23
|
+
Requires-Dist: httpx-sse>=0.4
|
|
24
|
+
Requires-Dist: httpx>=0.27
|
|
25
|
+
Requires-Dist: pydantic>=2
|
|
26
|
+
Requires-Dist: typing-extensions>=4.8
|
|
27
|
+
Requires-Dist: websockets>=16.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-asyncio>=1.4.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=9.0.3; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.15.16; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# restless-stream
|
|
37
|
+
|
|
38
|
+
Official Python SDK for Restless Stream: <https://restlessapi.stream>
|
|
39
|
+
|
|
40
|
+
Restless Stream turns REST APIs into live Server-Sent Events and WebSocket streams. This package provides synchronous REST and SSE support, asynchronous REST, SSE, and WebSocket support, typed Pydantic models, runtime URL builders, and HMAC signature helpers.
|
|
41
|
+
|
|
42
|
+
## Requirements
|
|
43
|
+
|
|
44
|
+
- Python `>=3.9`.
|
|
45
|
+
- A Restless Stream account and API key.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python -m pip install restless-stream
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For local development from this repository:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python -m pip install -e "packages/python/core[dev]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Getting Started
|
|
60
|
+
|
|
61
|
+
### Async Client
|
|
62
|
+
|
|
63
|
+
Use `AsyncRestlessStreamClient` when you need async REST calls, SSE subscriptions, or WebSocket subscriptions.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import asyncio
|
|
67
|
+
import os
|
|
68
|
+
|
|
69
|
+
from restless_stream import AsyncRestlessStreamClient
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def main() -> None:
|
|
73
|
+
async with AsyncRestlessStreamClient(api_key=os.environ["RESTLESS_API_KEY"]) as client:
|
|
74
|
+
stream = await client.streams.create(
|
|
75
|
+
name="Orders",
|
|
76
|
+
description="Live order feed",
|
|
77
|
+
status="ACTIVE",
|
|
78
|
+
method="GET",
|
|
79
|
+
url="https://api.example.com/orders",
|
|
80
|
+
payload_mode="FULL_DATA",
|
|
81
|
+
polling_interval=30,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
async for event in client.streams.subscribe_sse(stream.sse_url, reconnect=False):
|
|
85
|
+
print(event.type, event.data)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
asyncio.run(main())
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Sync Client
|
|
92
|
+
|
|
93
|
+
Use `RestlessStreamClient` for synchronous REST calls and SSE subscriptions.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import os
|
|
97
|
+
|
|
98
|
+
from restless_stream import RestlessStreamClient
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
with RestlessStreamClient(api_key=os.environ["RESTLESS_API_KEY"]) as client:
|
|
102
|
+
streams = client.streams.list(limit=20, offset=0)
|
|
103
|
+
|
|
104
|
+
for stream in streams.streams:
|
|
105
|
+
print(stream.id, stream.name)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Client Configuration
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from restless_stream import AsyncRestlessStreamClient, RestlessStreamClient
|
|
112
|
+
|
|
113
|
+
client = RestlessStreamClient(
|
|
114
|
+
api_key="rs_...",
|
|
115
|
+
base_url="https://api.restlessapi.stream",
|
|
116
|
+
stream_base_url="https://stream.restlessapi.stream",
|
|
117
|
+
timeout=30.0,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
async_client = AsyncRestlessStreamClient(
|
|
121
|
+
api_key="rs_...",
|
|
122
|
+
timeout=30.0,
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Option | Description |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| `api_key` | Sends `x-api-key` on REST requests and `Authorization: Bearer <key>` on stream runtime requests. |
|
|
129
|
+
| `base_url` | REST API base URL. Defaults to `https://api.restlessapi.stream`. |
|
|
130
|
+
| `stream_base_url` | Runtime stream base URL. Defaults to `https://stream.restlessapi.stream`. |
|
|
131
|
+
| `timeout` | HTTP timeout in seconds for the owned `httpx` client. Defaults to `30.0`. |
|
|
132
|
+
| `http_client` | Optional `httpx.Client` or `httpx.AsyncClient`. When provided, the SDK does not close it. |
|
|
133
|
+
|
|
134
|
+
Both clients support context managers. Use `close()` for the sync client and `await aclose()` for the async client when you do not use a context manager.
|
|
135
|
+
|
|
136
|
+
When creating or updating streams, the SDK adds `apiKey` to the request body when the client has an API key and the body does not already include `apiKey` or `api_key`.
|
|
137
|
+
|
|
138
|
+
## Stream Management
|
|
139
|
+
|
|
140
|
+
All stream management methods are available as top-level client methods and through `client.streams`.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
stream = await client.streams.create(
|
|
144
|
+
name="Orders",
|
|
145
|
+
description="Live order feed",
|
|
146
|
+
status="ACTIVE",
|
|
147
|
+
method="GET",
|
|
148
|
+
url="https://api.example.com/orders",
|
|
149
|
+
headers={"Accept": "application/json"},
|
|
150
|
+
payload_mode="FULL_DATA",
|
|
151
|
+
polling_interval=30,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
await client.streams.update(stream.id, name="Orders v2", polling_interval=60)
|
|
155
|
+
await client.streams.stop(stream.id)
|
|
156
|
+
await client.streams.start(stream.id)
|
|
157
|
+
|
|
158
|
+
usage = await client.streams.credit_usage_stats(stream.id)
|
|
159
|
+
snippets = await client.streams.connection_snippets(stream_id=stream.id, language="python")
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Top-level method | Resource method | Description |
|
|
163
|
+
| --- | --- | --- |
|
|
164
|
+
| `list_streams(limit=20, offset=0)` | `streams.list(...)` | List streams. |
|
|
165
|
+
| `get_stream(stream_id)` | `streams.get(stream_id)` | Get one stream. |
|
|
166
|
+
| `create_stream(data=None, **kwargs)` | `streams.create(...)` | Create a persisted stream. |
|
|
167
|
+
| `update_stream(stream_id, data=None, **kwargs)` | `streams.update(...)` | Patch stream configuration. |
|
|
168
|
+
| `start_stream(stream_id)` | `streams.start(stream_id)` | Mark a stream active. |
|
|
169
|
+
| `stop_stream(stream_id)` | `streams.stop(stream_id)` | Mark a stream inactive. |
|
|
170
|
+
| `delete_stream(stream_id)` | `streams.delete(stream_id)` | Delete a stream. |
|
|
171
|
+
| `validate_stream_api_key(api_key=None)` | `streams.validate_api_key(...)` | Validate an API key. Uses the client key when omitted. |
|
|
172
|
+
| `credit_usage_stats(stream_id)` | `streams.credit_usage_stats(...)` | Fetch credit usage totals and daily usage. |
|
|
173
|
+
| `connection_snippets(data=None, **kwargs)` | `streams.connection_snippets(...)` | Generate runtime URLs and snippets. |
|
|
174
|
+
| `direct_setup(data=None, **kwargs)` | `streams.direct_setup(...)` | Generate direct-stream commands, URLs, and snippets. |
|
|
175
|
+
| `direct_session(data=None, **kwargs)` | `streams.direct_session(...)` | Create or reuse a direct stream session. |
|
|
176
|
+
| `subscribe_sse(url, **kwargs)` | `streams.subscribe_sse(...)` | Subscribe to an SSE runtime URL. |
|
|
177
|
+
| `subscribe_direct_sse(**kwargs)` | `streams.subscribe_direct_sse(...)` | Build and subscribe to a direct SSE URL. |
|
|
178
|
+
| `subscribe_websocket(url, **kwargs)` | `streams.subscribe_websocket(...)` | Async client only. Subscribe to a WebSocket runtime URL. |
|
|
179
|
+
| `subscribe_direct_websocket(**kwargs)` | `streams.subscribe_direct_websocket(...)` | Async client only. Build and subscribe to a direct WebSocket URL. |
|
|
180
|
+
|
|
181
|
+
Request bodies may be mappings, Pydantic models, or keyword arguments. Snake-case keys are converted to the camel-case field names expected by the API.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
await client.streams.create(
|
|
185
|
+
name="Orders",
|
|
186
|
+
description="",
|
|
187
|
+
status="ACTIVE",
|
|
188
|
+
method="POST",
|
|
189
|
+
url="https://api.example.com/orders/search",
|
|
190
|
+
body={"status": "open"},
|
|
191
|
+
payload_mode="JSON_PATCH",
|
|
192
|
+
polling_interval=30,
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Direct Streams
|
|
197
|
+
|
|
198
|
+
Direct streams let you stream a REST endpoint without creating a persisted stream first.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
async for event in client.streams.subscribe_direct_sse(
|
|
202
|
+
url="https://api.example.com/orders",
|
|
203
|
+
method="GET",
|
|
204
|
+
polling_interval=30,
|
|
205
|
+
reconnect=False,
|
|
206
|
+
):
|
|
207
|
+
print(event)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Create a reusable direct session when you need a stable URL.
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
session = await client.streams.direct_session(
|
|
214
|
+
dedupe_key="orders-feed-v1",
|
|
215
|
+
method="GET",
|
|
216
|
+
url="https://api.example.com/orders",
|
|
217
|
+
polling_interval=30,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
async for event in client.streams.subscribe_sse(session.sse_url):
|
|
221
|
+
print(event.data)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Generate setup commands and code snippets without subscribing.
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
setup = await client.streams.direct_setup(
|
|
228
|
+
method="POST",
|
|
229
|
+
url="https://api.example.com/search",
|
|
230
|
+
body={"query": "restless"},
|
|
231
|
+
language="python",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
print(setup.commands.header.curl)
|
|
235
|
+
print(setup.runtime.direct_sse_url)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Direct stream inputs support `url`, `method`, `headers`, `body`, `jq_filter`, `payload_mode`, `polling_interval`, `polling_strategies`, and `api_key`.
|
|
239
|
+
|
|
240
|
+
## SSE Streaming
|
|
241
|
+
|
|
242
|
+
SSE subscriptions yield `StreamEvent` Pydantic models.
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
for event in sync_client.streams.subscribe_sse(
|
|
246
|
+
"https://stream.restlessapi.stream?streamId=stream_123",
|
|
247
|
+
cursor="1700000000000",
|
|
248
|
+
reconnect=False,
|
|
249
|
+
):
|
|
250
|
+
print(event.type, event.data)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
async for event in async_client.streams.subscribe_sse(
|
|
255
|
+
"https://stream.restlessapi.stream?streamId=stream_123",
|
|
256
|
+
since="2026-01-01T00:00:00Z",
|
|
257
|
+
max_reconnects=3,
|
|
258
|
+
retry_seconds=2,
|
|
259
|
+
):
|
|
260
|
+
print(event.type, event.meta.timestamp)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
SSE options:
|
|
264
|
+
|
|
265
|
+
| Option | Description |
|
|
266
|
+
| --- | --- |
|
|
267
|
+
| `cursor` | Initial cursor or SSE event ID. Updated automatically from received events. |
|
|
268
|
+
| `since` | Initial timestamp or cursor used until an event cursor is observed. |
|
|
269
|
+
| `reconnect` | Reconnect after the stream ends or errors. Defaults to `True`. |
|
|
270
|
+
| `max_reconnects` | Maximum reconnect attempts. `None` means unbounded. |
|
|
271
|
+
| `retry_seconds` | Delay between reconnect attempts. Defaults to `1.0`. |
|
|
272
|
+
|
|
273
|
+
The SDK sends `Authorization: Bearer <api_key>` for runtime subscriptions when the client has an API key.
|
|
274
|
+
|
|
275
|
+
## WebSocket Streaming
|
|
276
|
+
|
|
277
|
+
WebSocket subscriptions are async-only.
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
async for event in async_client.streams.subscribe_websocket(
|
|
281
|
+
"wss://stream.restlessapi.stream/ws?streamId=stream_123",
|
|
282
|
+
cursor="42",
|
|
283
|
+
reconnect=True,
|
|
284
|
+
max_reconnects=3,
|
|
285
|
+
):
|
|
286
|
+
print(event.type, event.data)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Direct WebSocket subscription:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
async for event in async_client.streams.subscribe_direct_websocket(
|
|
293
|
+
url="https://api.example.com/orders",
|
|
294
|
+
method="GET",
|
|
295
|
+
polling_interval=30,
|
|
296
|
+
connect_kwargs={"open_timeout": 10},
|
|
297
|
+
):
|
|
298
|
+
print(event)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
WebSocket options include all SSE runtime options plus `connect_kwargs`, which are passed to `websockets.connect`. Do not include `additional_headers` or `extra_headers` in `connect_kwargs` when the SDK client has an `api_key`; the SDK owns the runtime Authorization header in that case.
|
|
302
|
+
|
|
303
|
+
## Runtime URL Helpers
|
|
304
|
+
|
|
305
|
+
Build runtime URLs without creating a client.
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
from restless_stream import (
|
|
309
|
+
build_direct_sse_url,
|
|
310
|
+
build_direct_websocket_url,
|
|
311
|
+
build_sse_url,
|
|
312
|
+
build_websocket_url,
|
|
313
|
+
to_websocket_url,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
sse_url = build_sse_url(stream_id="stream_123", since="2026-01-01T00:00:00Z")
|
|
317
|
+
ws_url = build_websocket_url(session_id="session_123")
|
|
318
|
+
|
|
319
|
+
direct_sse_url = build_direct_sse_url(
|
|
320
|
+
url="https://api.example.com/orders",
|
|
321
|
+
method="POST",
|
|
322
|
+
body={"status": "open"},
|
|
323
|
+
payload_mode="JSON_PATCH",
|
|
324
|
+
polling_interval=30,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
direct_ws_url = build_direct_websocket_url(url="https://api.example.com/orders")
|
|
328
|
+
converted_ws_url = to_websocket_url(sse_url)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
`build_sse_url` requires exactly one of `stream_id` or `session_id`.
|
|
332
|
+
|
|
333
|
+
## Models
|
|
334
|
+
|
|
335
|
+
Response models are Pydantic v2 models. Python attributes use snake case and accept the API's camel-case aliases.
|
|
336
|
+
|
|
337
|
+
Common models and enums:
|
|
338
|
+
|
|
339
|
+
| Export | Description |
|
|
340
|
+
| --- | --- |
|
|
341
|
+
| `RestlessModel` | Base Pydantic model with camel-case aliases and extra fields allowed. |
|
|
342
|
+
| `HttpMethod` | `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`. |
|
|
343
|
+
| `StreamStatus` | `ACTIVE` or `INACTIVE`. |
|
|
344
|
+
| `PayloadMode` | `FULL_DATA` or `JSON_PATCH`. |
|
|
345
|
+
| `PollingStrategy` | Time-windowed polling strategy. |
|
|
346
|
+
| `Stream` | Persisted stream response. |
|
|
347
|
+
| `StreamsResponse` | Stream list response with pagination info. |
|
|
348
|
+
| `BaseActionResponse` | Generic action response. |
|
|
349
|
+
| `StreamCreditUsageStats`, `DailyCreditUsage`, `CreditUsageChargeTypeBreakdown` | Credit usage responses. |
|
|
350
|
+
| `ConnectionSnippetsResponse` | Managed stream runtime URLs and snippets. |
|
|
351
|
+
| `DirectSetupResponse` | Direct setup commands, runtime URLs, snippets, and stream config. |
|
|
352
|
+
| `DirectSessionResponse` | Direct session runtime URLs and expiry. |
|
|
353
|
+
| `StreamEvent`, `StreamEventMeta`, `StreamErrorDetail` | Runtime event models. |
|
|
354
|
+
|
|
355
|
+
Runtime update events contain `type`, `meta`, `data`, optional `signature`, and optional `event_id`. Error events contain `type`, `meta`, and `error`.
|
|
356
|
+
|
|
357
|
+
## Error Handling
|
|
358
|
+
|
|
359
|
+
REST API failures raise `RestlessStreamAPIError`.
|
|
360
|
+
|
|
361
|
+
```python
|
|
362
|
+
from restless_stream import RestlessStreamAPIError
|
|
363
|
+
|
|
364
|
+
try:
|
|
365
|
+
stream = client.streams.get("missing")
|
|
366
|
+
except RestlessStreamAPIError as error:
|
|
367
|
+
print(error.status_code, error.message)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Invalid runtime event payloads raise `RestlessStreamParseError`. Both exceptions inherit from `RestlessStreamError`.
|
|
371
|
+
|
|
372
|
+
## HMAC Helpers
|
|
373
|
+
|
|
374
|
+
Use HMAC helpers to compute or verify Restless Stream HMAC-SHA256 signatures over JSON-compatible payloads.
|
|
375
|
+
|
|
376
|
+
```python
|
|
377
|
+
from restless_stream import compute_hmac_signature, verify_hmac_signature
|
|
378
|
+
|
|
379
|
+
payload = {"id": "order_123", "total": 42}
|
|
380
|
+
signature = compute_hmac_signature("secret", payload)
|
|
381
|
+
|
|
382
|
+
if verify_hmac_signature("secret", payload, signature):
|
|
383
|
+
print("valid")
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
When verifying a runtime event signature, pass the same JSON payload your integration signs and the event's `signature` value.
|
|
387
|
+
|
|
388
|
+
## Public Exports
|
|
389
|
+
|
|
390
|
+
| Export | Purpose |
|
|
391
|
+
| --- | --- |
|
|
392
|
+
| `RestlessStreamClient` | Synchronous REST and SSE client. |
|
|
393
|
+
| `AsyncRestlessStreamClient` | Asynchronous REST, SSE, and WebSocket client. |
|
|
394
|
+
| `RestlessStreamError` | Base SDK exception. |
|
|
395
|
+
| `RestlessStreamAPIError` | REST API error exception. |
|
|
396
|
+
| `RestlessStreamParseError` | Runtime event parsing exception. |
|
|
397
|
+
| `compute_hmac_signature` | Computes an HMAC-SHA256 signature for a JSON-compatible payload. |
|
|
398
|
+
| `verify_hmac_signature` | Constant-time HMAC signature verification. |
|
|
399
|
+
| `DEFAULT_STREAM_BASE_URL` | Default runtime stream base URL. |
|
|
400
|
+
| `build_sse_url`, `build_websocket_url` | Managed stream runtime URL builders. |
|
|
401
|
+
| `build_direct_sse_url`, `build_direct_websocket_url` | Direct stream runtime URL builders. |
|
|
402
|
+
| `to_websocket_url` | Converts an SSE URL to a WebSocket URL. |
|
|
403
|
+
| Models and enums | Pydantic response models and enum types listed above. |
|
|
404
|
+
|
|
405
|
+
## Development
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
python -m pip install -e "packages/python/core[dev]"
|
|
409
|
+
python -m ruff check packages/python/core packages/python/examples
|
|
410
|
+
cd packages/python/core
|
|
411
|
+
python -m pytest tests
|
|
412
|
+
```
|