restless-stream 0.1.1__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.
- restless_stream/__init__.py +65 -0
- restless_stream/_async_client.py +145 -0
- restless_stream/_client.py +7 -0
- restless_stream/_constants.py +1 -0
- restless_stream/_errors.py +18 -0
- restless_stream/_hmac.py +28 -0
- restless_stream/_models.py +196 -0
- restless_stream/_resources.py +133 -0
- restless_stream/_streaming.py +179 -0
- restless_stream/_sync_client.py +132 -0
- restless_stream/_urls.py +84 -0
- restless_stream/_utils.py +89 -0
- restless_stream/py.typed +0 -0
- restless_stream-0.1.1.dist-info/METADATA +412 -0
- restless_stream-0.1.1.dist-info/RECORD +16 -0
- restless_stream-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Python SDK for Restless Stream."""
|
|
2
|
+
|
|
3
|
+
from ._client import AsyncRestlessStreamClient, RestlessStreamClient
|
|
4
|
+
from ._errors import RestlessStreamAPIError, RestlessStreamError, RestlessStreamParseError
|
|
5
|
+
from ._hmac import compute_hmac_signature, verify_hmac_signature
|
|
6
|
+
from ._models import (
|
|
7
|
+
BaseActionResponse,
|
|
8
|
+
ConnectionSnippetsResponse,
|
|
9
|
+
CreditUsageChargeTypeBreakdown,
|
|
10
|
+
DailyCreditUsage,
|
|
11
|
+
DirectSessionResponse,
|
|
12
|
+
DirectSetupResponse,
|
|
13
|
+
HttpMethod,
|
|
14
|
+
PayloadMode,
|
|
15
|
+
PollingStrategy,
|
|
16
|
+
RestlessModel,
|
|
17
|
+
Stream,
|
|
18
|
+
StreamCreditUsageStats,
|
|
19
|
+
StreamErrorDetail,
|
|
20
|
+
StreamEvent,
|
|
21
|
+
StreamEventMeta,
|
|
22
|
+
StreamsResponse,
|
|
23
|
+
StreamStatus,
|
|
24
|
+
)
|
|
25
|
+
from ._urls import (
|
|
26
|
+
DEFAULT_STREAM_BASE_URL,
|
|
27
|
+
build_direct_sse_url,
|
|
28
|
+
build_direct_websocket_url,
|
|
29
|
+
build_sse_url,
|
|
30
|
+
build_websocket_url,
|
|
31
|
+
to_websocket_url,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"AsyncRestlessStreamClient",
|
|
36
|
+
"BaseActionResponse",
|
|
37
|
+
"ConnectionSnippetsResponse",
|
|
38
|
+
"CreditUsageChargeTypeBreakdown",
|
|
39
|
+
"DEFAULT_STREAM_BASE_URL",
|
|
40
|
+
"DailyCreditUsage",
|
|
41
|
+
"DirectSessionResponse",
|
|
42
|
+
"DirectSetupResponse",
|
|
43
|
+
"HttpMethod",
|
|
44
|
+
"PayloadMode",
|
|
45
|
+
"PollingStrategy",
|
|
46
|
+
"RestlessModel",
|
|
47
|
+
"RestlessStreamAPIError",
|
|
48
|
+
"RestlessStreamClient",
|
|
49
|
+
"RestlessStreamError",
|
|
50
|
+
"RestlessStreamParseError",
|
|
51
|
+
"Stream",
|
|
52
|
+
"StreamCreditUsageStats",
|
|
53
|
+
"StreamErrorDetail",
|
|
54
|
+
"StreamEvent",
|
|
55
|
+
"StreamEventMeta",
|
|
56
|
+
"StreamStatus",
|
|
57
|
+
"StreamsResponse",
|
|
58
|
+
"compute_hmac_signature",
|
|
59
|
+
"verify_hmac_signature",
|
|
60
|
+
"build_direct_sse_url",
|
|
61
|
+
"build_direct_websocket_url",
|
|
62
|
+
"build_sse_url",
|
|
63
|
+
"build_websocket_url",
|
|
64
|
+
"to_websocket_url",
|
|
65
|
+
]
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import AsyncIterator, Mapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from ._constants import DEFAULT_API_BASE_URL
|
|
9
|
+
from ._models import (
|
|
10
|
+
BaseActionResponse,
|
|
11
|
+
ConnectionSnippetsResponse,
|
|
12
|
+
DirectSessionResponse,
|
|
13
|
+
DirectSetupResponse,
|
|
14
|
+
Stream,
|
|
15
|
+
StreamCreditUsageStats,
|
|
16
|
+
StreamEvent,
|
|
17
|
+
StreamsResponse,
|
|
18
|
+
)
|
|
19
|
+
from ._resources import AsyncStreams
|
|
20
|
+
from ._streaming import async_sse_events, websocket_events
|
|
21
|
+
from ._urls import DEFAULT_STREAM_BASE_URL, build_direct_sse_url, build_direct_websocket_url
|
|
22
|
+
from ._utils import dump_body, pop_stream_runtime_options, raise_for_status, with_api_key
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AsyncRestlessStreamClient:
|
|
26
|
+
"""Asynchronous REST, SSE, and WebSocket client."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
api_key: str | None = None,
|
|
31
|
+
*,
|
|
32
|
+
base_url: str = DEFAULT_API_BASE_URL,
|
|
33
|
+
stream_base_url: str = DEFAULT_STREAM_BASE_URL,
|
|
34
|
+
timeout: float = 30.0,
|
|
35
|
+
http_client: httpx.AsyncClient | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
self.api_key = api_key
|
|
38
|
+
self.stream_base_url = stream_base_url.rstrip("/")
|
|
39
|
+
self._owns_client = http_client is None
|
|
40
|
+
self._client = http_client or httpx.AsyncClient(
|
|
41
|
+
base_url=base_url.rstrip("/"),
|
|
42
|
+
timeout=timeout,
|
|
43
|
+
)
|
|
44
|
+
if api_key:
|
|
45
|
+
self._client.headers.setdefault("x-api-key", api_key)
|
|
46
|
+
self.streams = AsyncStreams(self)
|
|
47
|
+
|
|
48
|
+
async def aclose(self) -> None:
|
|
49
|
+
if self._owns_client:
|
|
50
|
+
await self._client.aclose()
|
|
51
|
+
|
|
52
|
+
async def __aenter__(self) -> AsyncRestlessStreamClient:
|
|
53
|
+
return self
|
|
54
|
+
|
|
55
|
+
async def __aexit__(self, *_: object) -> None:
|
|
56
|
+
await self.aclose()
|
|
57
|
+
|
|
58
|
+
async def _request(self, method: str, path: str, **kwargs: Any) -> Any:
|
|
59
|
+
response = await self._client.request(method, path, **kwargs)
|
|
60
|
+
raise_for_status(response)
|
|
61
|
+
if response.status_code == 204 or not response.content:
|
|
62
|
+
return None
|
|
63
|
+
return response.json()
|
|
64
|
+
|
|
65
|
+
async def list_streams(self, *, limit: int = 20, offset: int = 0) -> StreamsResponse:
|
|
66
|
+
return StreamsResponse.model_validate(
|
|
67
|
+
await self._request("GET", "/streams", params={"limit": limit, "offset": offset})
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
async def get_stream(self, stream_id: str) -> Stream:
|
|
71
|
+
return Stream.model_validate(await self._request("GET", f"/streams/{stream_id}"))
|
|
72
|
+
|
|
73
|
+
async def create_stream(self, data: Mapping[str, Any] | None = None, **kwargs: Any) -> Stream:
|
|
74
|
+
body = with_api_key(dump_body(data, kwargs), self.api_key)
|
|
75
|
+
return Stream.model_validate(await self._request("POST", "/streams", json=body))
|
|
76
|
+
|
|
77
|
+
async def update_stream(
|
|
78
|
+
self, stream_id: str, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
79
|
+
) -> Stream:
|
|
80
|
+
body = with_api_key(dump_body(data, kwargs), self.api_key)
|
|
81
|
+
return Stream.model_validate(await self._request("PATCH", f"/streams/{stream_id}", json=body))
|
|
82
|
+
|
|
83
|
+
async def start_stream(self, stream_id: str) -> Stream:
|
|
84
|
+
return Stream.model_validate(await self._request("POST", f"/streams/{stream_id}/start"))
|
|
85
|
+
|
|
86
|
+
async def stop_stream(self, stream_id: str) -> Stream:
|
|
87
|
+
return Stream.model_validate(await self._request("POST", f"/streams/{stream_id}/stop"))
|
|
88
|
+
|
|
89
|
+
async def delete_stream(self, stream_id: str) -> BaseActionResponse:
|
|
90
|
+
return BaseActionResponse.model_validate(await self._request("DELETE", f"/streams/{stream_id}"))
|
|
91
|
+
|
|
92
|
+
async def validate_stream_api_key(self, api_key: str | None = None) -> BaseActionResponse:
|
|
93
|
+
return BaseActionResponse.model_validate(
|
|
94
|
+
await self._request(
|
|
95
|
+
"POST", "/streams/validate-api-key", json={"apiKey": api_key or self.api_key}
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
async def credit_usage_stats(self, stream_id: str) -> StreamCreditUsageStats:
|
|
100
|
+
return StreamCreditUsageStats.model_validate(
|
|
101
|
+
await self._request("GET", f"/streams/{stream_id}/credit-usage-stats")
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
async def connection_snippets(
|
|
105
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
106
|
+
) -> ConnectionSnippetsResponse:
|
|
107
|
+
return ConnectionSnippetsResponse.model_validate(
|
|
108
|
+
await self._request("POST", "/streams/connection-snippets", json=dump_body(data, kwargs))
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
async def direct_setup(
|
|
112
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
113
|
+
) -> DirectSetupResponse:
|
|
114
|
+
return DirectSetupResponse.model_validate(
|
|
115
|
+
await self._request("POST", "/streams/direct/setup", json=dump_body(data, kwargs))
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
async def direct_session(
|
|
119
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
120
|
+
) -> DirectSessionResponse:
|
|
121
|
+
return DirectSessionResponse.model_validate(
|
|
122
|
+
await self._request("POST", "/streams/direct/sessions", json=dump_body(data, kwargs))
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def subscribe_sse(self, url: str, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
126
|
+
return async_sse_events(self._client, url, api_key=self.api_key, **kwargs)
|
|
127
|
+
|
|
128
|
+
def subscribe_websocket(self, url: str, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
129
|
+
return websocket_events(url, api_key=self.api_key, **kwargs)
|
|
130
|
+
|
|
131
|
+
def subscribe_direct_sse(self, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
132
|
+
stream_base_url = kwargs.pop("stream_base_url", self.stream_base_url)
|
|
133
|
+
stream_options = pop_stream_runtime_options(kwargs)
|
|
134
|
+
return self.subscribe_sse(
|
|
135
|
+
build_direct_sse_url(stream_base_url=stream_base_url, **kwargs),
|
|
136
|
+
**stream_options,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def subscribe_direct_websocket(self, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
140
|
+
stream_base_url = kwargs.pop("stream_base_url", self.stream_base_url)
|
|
141
|
+
stream_options = pop_stream_runtime_options(kwargs, websocket=True)
|
|
142
|
+
return self.subscribe_websocket(
|
|
143
|
+
build_direct_websocket_url(stream_base_url=stream_base_url, **kwargs),
|
|
144
|
+
**stream_options,
|
|
145
|
+
)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ._async_client import AsyncRestlessStreamClient
|
|
4
|
+
from ._constants import DEFAULT_API_BASE_URL
|
|
5
|
+
from ._sync_client import RestlessStreamClient
|
|
6
|
+
|
|
7
|
+
__all__ = ["AsyncRestlessStreamClient", "DEFAULT_API_BASE_URL", "RestlessStreamClient"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DEFAULT_API_BASE_URL = "https://api.restlessapi.stream"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RestlessStreamError(Exception):
|
|
5
|
+
"""Base SDK exception."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RestlessStreamAPIError(RestlessStreamError):
|
|
9
|
+
"""Raised when the REST API returns a non-successful response."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, status_code: int, message: str) -> None:
|
|
12
|
+
super().__init__(f"Restless Stream API error {status_code}: {message}")
|
|
13
|
+
self.status_code = status_code
|
|
14
|
+
self.message = message
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RestlessStreamParseError(RestlessStreamError):
|
|
18
|
+
"""Raised when a runtime event cannot be parsed."""
|
restless_stream/_hmac.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hmac
|
|
4
|
+
import json
|
|
5
|
+
from hashlib import sha256
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _canonical_json_bytes(value: Any) -> bytes:
|
|
10
|
+
return json.dumps(
|
|
11
|
+
value,
|
|
12
|
+
ensure_ascii=False,
|
|
13
|
+
separators=(",", ":"),
|
|
14
|
+
sort_keys=True,
|
|
15
|
+
).encode("utf-8")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def compute_hmac_signature(secret: str, payload: Any) -> str:
|
|
19
|
+
"""Return the Restless Stream HMAC-SHA256 signature for a JSON payload."""
|
|
20
|
+
|
|
21
|
+
return hmac.new(secret.encode("utf-8"), _canonical_json_bytes(payload), sha256).hexdigest()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def verify_hmac_signature(secret: str, payload: Any, signature: str) -> bool:
|
|
25
|
+
"""Constant-time verification for a runtime event `signature` value."""
|
|
26
|
+
|
|
27
|
+
expected = compute_hmac_signature(secret, payload)
|
|
28
|
+
return hmac.compare_digest(expected, signature)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def to_camel(value: str) -> str:
|
|
10
|
+
head, *tail = value.split("_")
|
|
11
|
+
return head + "".join(part[:1].upper() + part[1:] for part in tail)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RestlessModel(BaseModel):
|
|
15
|
+
model_config = ConfigDict(
|
|
16
|
+
alias_generator=to_camel,
|
|
17
|
+
extra="allow",
|
|
18
|
+
populate_by_name=True,
|
|
19
|
+
use_enum_values=True,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HttpMethod(str, Enum):
|
|
24
|
+
GET = "GET"
|
|
25
|
+
POST = "POST"
|
|
26
|
+
PUT = "PUT"
|
|
27
|
+
PATCH = "PATCH"
|
|
28
|
+
DELETE = "DELETE"
|
|
29
|
+
OPTIONS = "OPTIONS"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class StreamStatus(str, Enum):
|
|
33
|
+
ACTIVE = "ACTIVE"
|
|
34
|
+
INACTIVE = "INACTIVE"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PayloadMode(str, Enum):
|
|
38
|
+
FULL_DATA = "FULL_DATA"
|
|
39
|
+
JSON_PATCH = "JSON_PATCH"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PollingStrategy(RestlessModel):
|
|
43
|
+
start_time: str
|
|
44
|
+
end_time: str
|
|
45
|
+
interval_seconds: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Stream(RestlessModel):
|
|
49
|
+
id: str
|
|
50
|
+
business_id: str | None = None
|
|
51
|
+
name: str
|
|
52
|
+
description: str | None = None
|
|
53
|
+
status: StreamStatus
|
|
54
|
+
method: HttpMethod
|
|
55
|
+
url: str
|
|
56
|
+
headers: dict[str, Any] | None = None
|
|
57
|
+
body: dict[str, Any] | None = None
|
|
58
|
+
payload_mode: PayloadMode
|
|
59
|
+
jq_filter: str | None = None
|
|
60
|
+
hmac_signature_enabled: bool = False
|
|
61
|
+
polling_interval: int
|
|
62
|
+
polling_strategies: list[PollingStrategy] | None = None
|
|
63
|
+
created_at: str | None = None
|
|
64
|
+
updated_at: str | None = None
|
|
65
|
+
ws_url: str | None = None
|
|
66
|
+
sse_url: str | None = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class PaginationInfo(RestlessModel):
|
|
70
|
+
has_next_page: bool = False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class StreamsResponse(RestlessModel):
|
|
74
|
+
streams: list[Stream]
|
|
75
|
+
pagination_info: PaginationInfo = Field(default_factory=PaginationInfo)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class BaseActionResponse(RestlessModel):
|
|
79
|
+
id: str
|
|
80
|
+
affected: bool
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CreditUsageChargeTypeBreakdown(RestlessModel):
|
|
84
|
+
charge_type: str
|
|
85
|
+
credits_used: int
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class DailyCreditUsage(RestlessModel):
|
|
89
|
+
date: str
|
|
90
|
+
credits_used: int
|
|
91
|
+
charge_type_breakdown: list[CreditUsageChargeTypeBreakdown]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class StreamCreditUsageStats(RestlessModel):
|
|
95
|
+
total_burned_last_week: int
|
|
96
|
+
total_burned_last_month: int
|
|
97
|
+
daily_usage: list[DailyCreditUsage]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class SnippetSet(RestlessModel):
|
|
101
|
+
curl: str
|
|
102
|
+
wget: str
|
|
103
|
+
sse: str
|
|
104
|
+
websocket: str
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class SnippetLanguageOption(RestlessModel):
|
|
108
|
+
label: str
|
|
109
|
+
value: str
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SnippetPayload(RestlessModel):
|
|
113
|
+
available_languages: list[SnippetLanguageOption]
|
|
114
|
+
selected_language: str
|
|
115
|
+
selected_snippets: SnippetSet
|
|
116
|
+
snippets_by_language: dict[str, SnippetSet] | None = None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class RuntimeUrls(RestlessModel):
|
|
120
|
+
sse_url: str
|
|
121
|
+
websocket_url: str
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ConnectionSnippetsResponse(RestlessModel):
|
|
125
|
+
runtime: RuntimeUrls
|
|
126
|
+
snippets: SnippetPayload
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class DirectCommandSet(RestlessModel):
|
|
130
|
+
curl: str
|
|
131
|
+
wget: str
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class DirectCommands(RestlessModel):
|
|
135
|
+
header: DirectCommandSet
|
|
136
|
+
url_param: DirectCommandSet
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class DirectSetupRuntimeAuth(RestlessModel):
|
|
140
|
+
api_key_location: str
|
|
141
|
+
header_example: str
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class DirectSetupRuntime(RestlessModel):
|
|
145
|
+
auth: DirectSetupRuntimeAuth
|
|
146
|
+
direct_sse_url: str
|
|
147
|
+
direct_websocket_url: str
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class DirectSetupStreamConfig(RestlessModel):
|
|
151
|
+
target_url: str
|
|
152
|
+
method: HttpMethod
|
|
153
|
+
headers: dict[str, Any] | None = None
|
|
154
|
+
body: dict[str, Any] | None = None
|
|
155
|
+
jq_filter: str | None = None
|
|
156
|
+
payload_mode: PayloadMode | None = None
|
|
157
|
+
polling_interval: int | None = None
|
|
158
|
+
polling_strategies: list[PollingStrategy] | None = None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class DirectSetupResponse(RestlessModel):
|
|
162
|
+
commands: DirectCommands
|
|
163
|
+
runtime: DirectSetupRuntime
|
|
164
|
+
snippets: SnippetPayload
|
|
165
|
+
stream_config: DirectSetupStreamConfig
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class DirectSessionResponse(RestlessModel):
|
|
169
|
+
session_id: str
|
|
170
|
+
ws_url: str
|
|
171
|
+
sse_url: str
|
|
172
|
+
expires_at: str
|
|
173
|
+
created: bool
|
|
174
|
+
dedupe_key: str
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class StreamEventMeta(RestlessModel):
|
|
178
|
+
timestamp: str
|
|
179
|
+
attempt_id: str | None = None
|
|
180
|
+
status: int | None = None
|
|
181
|
+
latency_ms: int | None = None
|
|
182
|
+
payload_mode_fallback: str | None = None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class StreamErrorDetail(RestlessModel):
|
|
186
|
+
code: str
|
|
187
|
+
message: str
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class StreamEvent(RestlessModel):
|
|
191
|
+
type: str
|
|
192
|
+
meta: StreamEventMeta
|
|
193
|
+
data: Any | None = None
|
|
194
|
+
error: StreamErrorDetail | None = None
|
|
195
|
+
signature: str | None = None
|
|
196
|
+
event_id: str | None = None
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import AsyncIterator, Iterator, Mapping
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
from ._models import (
|
|
7
|
+
BaseActionResponse,
|
|
8
|
+
ConnectionSnippetsResponse,
|
|
9
|
+
DirectSessionResponse,
|
|
10
|
+
DirectSetupResponse,
|
|
11
|
+
Stream,
|
|
12
|
+
StreamCreditUsageStats,
|
|
13
|
+
StreamEvent,
|
|
14
|
+
StreamsResponse,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ._async_client import AsyncRestlessStreamClient
|
|
19
|
+
from ._sync_client import RestlessStreamClient
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SyncStreams:
|
|
23
|
+
def __init__(self, client: RestlessStreamClient) -> None:
|
|
24
|
+
self._client = client
|
|
25
|
+
|
|
26
|
+
def list(self, **kwargs: Any) -> StreamsResponse:
|
|
27
|
+
return self._client.list_streams(**kwargs)
|
|
28
|
+
|
|
29
|
+
def get(self, stream_id: str) -> Stream:
|
|
30
|
+
return self._client.get_stream(stream_id)
|
|
31
|
+
|
|
32
|
+
def create(self, data: Mapping[str, Any] | None = None, **kwargs: Any) -> Stream:
|
|
33
|
+
return self._client.create_stream(data, **kwargs)
|
|
34
|
+
|
|
35
|
+
def update(self, stream_id: str, data: Mapping[str, Any] | None = None, **kwargs: Any) -> Stream:
|
|
36
|
+
return self._client.update_stream(stream_id, data, **kwargs)
|
|
37
|
+
|
|
38
|
+
def start(self, stream_id: str) -> Stream:
|
|
39
|
+
return self._client.start_stream(stream_id)
|
|
40
|
+
|
|
41
|
+
def stop(self, stream_id: str) -> Stream:
|
|
42
|
+
return self._client.stop_stream(stream_id)
|
|
43
|
+
|
|
44
|
+
def delete(self, stream_id: str) -> BaseActionResponse:
|
|
45
|
+
return self._client.delete_stream(stream_id)
|
|
46
|
+
|
|
47
|
+
def validate_api_key(self, api_key: str | None = None) -> BaseActionResponse:
|
|
48
|
+
return self._client.validate_stream_api_key(api_key)
|
|
49
|
+
|
|
50
|
+
def credit_usage_stats(self, stream_id: str) -> StreamCreditUsageStats:
|
|
51
|
+
return self._client.credit_usage_stats(stream_id)
|
|
52
|
+
|
|
53
|
+
def connection_snippets(
|
|
54
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
55
|
+
) -> ConnectionSnippetsResponse:
|
|
56
|
+
return self._client.connection_snippets(data, **kwargs)
|
|
57
|
+
|
|
58
|
+
def direct_setup(
|
|
59
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
60
|
+
) -> DirectSetupResponse:
|
|
61
|
+
return self._client.direct_setup(data, **kwargs)
|
|
62
|
+
|
|
63
|
+
def direct_session(
|
|
64
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
65
|
+
) -> DirectSessionResponse:
|
|
66
|
+
return self._client.direct_session(data, **kwargs)
|
|
67
|
+
|
|
68
|
+
def subscribe_sse(self, url: str, **kwargs: Any) -> Iterator[StreamEvent]:
|
|
69
|
+
return self._client.subscribe_sse(url, **kwargs)
|
|
70
|
+
|
|
71
|
+
def subscribe_direct_sse(self, **kwargs: Any) -> Iterator[StreamEvent]:
|
|
72
|
+
return self._client.subscribe_direct_sse(**kwargs)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class AsyncStreams:
|
|
76
|
+
def __init__(self, client: AsyncRestlessStreamClient) -> None:
|
|
77
|
+
self._client = client
|
|
78
|
+
|
|
79
|
+
async def list(self, **kwargs: Any) -> StreamsResponse:
|
|
80
|
+
return await self._client.list_streams(**kwargs)
|
|
81
|
+
|
|
82
|
+
async def get(self, stream_id: str) -> Stream:
|
|
83
|
+
return await self._client.get_stream(stream_id)
|
|
84
|
+
|
|
85
|
+
async def create(self, data: Mapping[str, Any] | None = None, **kwargs: Any) -> Stream:
|
|
86
|
+
return await self._client.create_stream(data, **kwargs)
|
|
87
|
+
|
|
88
|
+
async def update(
|
|
89
|
+
self, stream_id: str, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
90
|
+
) -> Stream:
|
|
91
|
+
return await self._client.update_stream(stream_id, data, **kwargs)
|
|
92
|
+
|
|
93
|
+
async def start(self, stream_id: str) -> Stream:
|
|
94
|
+
return await self._client.start_stream(stream_id)
|
|
95
|
+
|
|
96
|
+
async def stop(self, stream_id: str) -> Stream:
|
|
97
|
+
return await self._client.stop_stream(stream_id)
|
|
98
|
+
|
|
99
|
+
async def delete(self, stream_id: str) -> BaseActionResponse:
|
|
100
|
+
return await self._client.delete_stream(stream_id)
|
|
101
|
+
|
|
102
|
+
async def validate_api_key(self, api_key: str | None = None) -> BaseActionResponse:
|
|
103
|
+
return await self._client.validate_stream_api_key(api_key)
|
|
104
|
+
|
|
105
|
+
async def credit_usage_stats(self, stream_id: str) -> StreamCreditUsageStats:
|
|
106
|
+
return await self._client.credit_usage_stats(stream_id)
|
|
107
|
+
|
|
108
|
+
async def connection_snippets(
|
|
109
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
110
|
+
) -> ConnectionSnippetsResponse:
|
|
111
|
+
return await self._client.connection_snippets(data, **kwargs)
|
|
112
|
+
|
|
113
|
+
async def direct_setup(
|
|
114
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
115
|
+
) -> DirectSetupResponse:
|
|
116
|
+
return await self._client.direct_setup(data, **kwargs)
|
|
117
|
+
|
|
118
|
+
async def direct_session(
|
|
119
|
+
self, data: Mapping[str, Any] | None = None, **kwargs: Any
|
|
120
|
+
) -> DirectSessionResponse:
|
|
121
|
+
return await self._client.direct_session(data, **kwargs)
|
|
122
|
+
|
|
123
|
+
def subscribe_sse(self, url: str, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
124
|
+
return self._client.subscribe_sse(url, **kwargs)
|
|
125
|
+
|
|
126
|
+
def subscribe_websocket(self, url: str, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
127
|
+
return self._client.subscribe_websocket(url, **kwargs)
|
|
128
|
+
|
|
129
|
+
def subscribe_direct_sse(self, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
130
|
+
return self._client.subscribe_direct_sse(**kwargs)
|
|
131
|
+
|
|
132
|
+
def subscribe_direct_websocket(self, **kwargs: Any) -> AsyncIterator[StreamEvent]:
|
|
133
|
+
return self._client.subscribe_direct_websocket(**kwargs)
|