jigsawstack 0.1.26__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.
- jigsawstack/__init__.py +232 -0
- jigsawstack/_client.py +62 -0
- jigsawstack/_config.py +17 -0
- jigsawstack/async_request.py +266 -0
- jigsawstack/audio.py +141 -0
- jigsawstack/custom_typing.py +574 -0
- jigsawstack/embedding.py +77 -0
- jigsawstack/exceptions.py +220 -0
- jigsawstack/geo.py +408 -0
- jigsawstack/prediction.py +94 -0
- jigsawstack/prompt_engine.py +318 -0
- jigsawstack/request.py +253 -0
- jigsawstack/search.py +161 -0
- jigsawstack/sentiment.py +100 -0
- jigsawstack/sql.py +91 -0
- jigsawstack/store.py +245 -0
- jigsawstack/summary.py +105 -0
- jigsawstack/translate.py +103 -0
- jigsawstack/validate.py +224 -0
- jigsawstack/version.py +8 -0
- jigsawstack/vision.py +104 -0
- jigsawstack/web.py +281 -0
- jigsawstack-0.1.26.dist-info/METADATA +130 -0
- jigsawstack-0.1.26.dist-info/RECORD +26 -0
- jigsawstack-0.1.26.dist-info/WHEEL +5 -0
- jigsawstack-0.1.26.dist-info/top_level.txt +1 -0
jigsawstack/__init__.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import os
|
|
3
|
+
from .audio import Audio, AsyncAudio
|
|
4
|
+
from .vision import Vision, AsyncVision
|
|
5
|
+
from .search import Search
|
|
6
|
+
from .prediction import Prediction, AsyncPrediction
|
|
7
|
+
from .sql import SQL, AsyncSQL
|
|
8
|
+
from .store import Store, AsyncStore
|
|
9
|
+
from .translate import Translate, AsyncTranslate
|
|
10
|
+
from .web import Web, AsyncWeb
|
|
11
|
+
from .sentiment import Sentiment, AsyncSentiment
|
|
12
|
+
from .validate import Validate, AsyncValidate
|
|
13
|
+
from .summary import Summary, AsyncSummary
|
|
14
|
+
from .geo import Geo, AsyncGeo
|
|
15
|
+
from .prompt_engine import PromptEngine, AsyncPromptEngine
|
|
16
|
+
from .embedding import Embedding, AsyncEmbedding
|
|
17
|
+
from .exceptions import JigsawStackError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class JigsawStack:
|
|
21
|
+
audio: Audio
|
|
22
|
+
vision: Vision
|
|
23
|
+
file: Store
|
|
24
|
+
web: Web
|
|
25
|
+
search: Search
|
|
26
|
+
geo: Geo
|
|
27
|
+
prompt_engine: PromptEngine
|
|
28
|
+
api_key: str
|
|
29
|
+
api_url: str
|
|
30
|
+
disable_request_logging: bool
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
api_key: Union[str, None] = None,
|
|
35
|
+
api_url: Union[str, None] = None,
|
|
36
|
+
disable_request_logging: Union[bool, None] = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
if api_key is None:
|
|
39
|
+
api_key = os.environ.get("JIGSAWSTACK_API_KEY")
|
|
40
|
+
|
|
41
|
+
if api_key is None:
|
|
42
|
+
raise ValueError(
|
|
43
|
+
"The api_key client option must be set either by passing api_key to the client or by setting the JIGSAWSTACK_API_KEY environment variable"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if api_url is None:
|
|
47
|
+
api_url = os.environ.get("JIGSAWSTACK_API_URL")
|
|
48
|
+
if api_url is None:
|
|
49
|
+
api_url = f"https://api.jigsawstack.com/v1"
|
|
50
|
+
|
|
51
|
+
self.api_key = api_key
|
|
52
|
+
self.api_url = api_url
|
|
53
|
+
|
|
54
|
+
self.audio = Audio(
|
|
55
|
+
api_key=api_key,
|
|
56
|
+
api_url=api_url,
|
|
57
|
+
disable_request_logging=disable_request_logging,
|
|
58
|
+
)
|
|
59
|
+
self.web = Web(
|
|
60
|
+
api_key=api_key,
|
|
61
|
+
api_url=api_url,
|
|
62
|
+
disable_request_logging=disable_request_logging,
|
|
63
|
+
)
|
|
64
|
+
self.sentiment = Sentiment(
|
|
65
|
+
api_key=api_key,
|
|
66
|
+
api_url=api_url,
|
|
67
|
+
disable_request_logging=disable_request_logging,
|
|
68
|
+
).analyze
|
|
69
|
+
self.validate = Validate(
|
|
70
|
+
api_key=api_key,
|
|
71
|
+
api_url=api_url,
|
|
72
|
+
disable_request_logging=disable_request_logging,
|
|
73
|
+
)
|
|
74
|
+
self.summary = Summary(
|
|
75
|
+
api_key=api_key,
|
|
76
|
+
api_url=api_url,
|
|
77
|
+
disable_request_logging=disable_request_logging,
|
|
78
|
+
).summarize
|
|
79
|
+
self.vision = Vision(
|
|
80
|
+
api_key=api_key,
|
|
81
|
+
api_url=api_url,
|
|
82
|
+
disable_request_logging=disable_request_logging,
|
|
83
|
+
)
|
|
84
|
+
self.prediction = Prediction(
|
|
85
|
+
api_key=api_key,
|
|
86
|
+
api_url=api_url,
|
|
87
|
+
disable_request_logging=disable_request_logging,
|
|
88
|
+
).predict
|
|
89
|
+
self.text_to_sql = SQL(
|
|
90
|
+
api_key=api_key,
|
|
91
|
+
api_url=api_url,
|
|
92
|
+
disable_request_logging=disable_request_logging,
|
|
93
|
+
).text_to_sql
|
|
94
|
+
self.store = Store(
|
|
95
|
+
api_key=api_key,
|
|
96
|
+
api_url=api_url,
|
|
97
|
+
disable_request_logging=disable_request_logging,
|
|
98
|
+
)
|
|
99
|
+
self.translate = Translate(
|
|
100
|
+
api_key=api_key,
|
|
101
|
+
api_url=api_url,
|
|
102
|
+
disable_request_logging=disable_request_logging,
|
|
103
|
+
).translate
|
|
104
|
+
self.geo = Geo(
|
|
105
|
+
api_key=api_key,
|
|
106
|
+
api_url=api_url,
|
|
107
|
+
disable_request_logging=disable_request_logging,
|
|
108
|
+
)
|
|
109
|
+
self.prompt_engine = PromptEngine(
|
|
110
|
+
api_key=api_key,
|
|
111
|
+
api_url=api_url,
|
|
112
|
+
disable_request_logging=disable_request_logging,
|
|
113
|
+
)
|
|
114
|
+
self.embedding = Embedding(
|
|
115
|
+
api_key=api_key,
|
|
116
|
+
api_url=api_url,
|
|
117
|
+
disable_request_logging=disable_request_logging,
|
|
118
|
+
).execute
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class AsyncJigsawStack:
|
|
122
|
+
geo: AsyncGeo
|
|
123
|
+
validate: AsyncValidate
|
|
124
|
+
web: AsyncWeb
|
|
125
|
+
audio: AsyncAudio
|
|
126
|
+
vision: AsyncVision
|
|
127
|
+
store: AsyncStore
|
|
128
|
+
prompt_engine: AsyncPromptEngine
|
|
129
|
+
api_key: str
|
|
130
|
+
api_url: str
|
|
131
|
+
disable_request_logging: bool
|
|
132
|
+
|
|
133
|
+
def __init__(
|
|
134
|
+
self,
|
|
135
|
+
api_key: Union[str, None] = None,
|
|
136
|
+
api_url: Union[str, None] = None,
|
|
137
|
+
disable_request_logging: Union[bool, None] = None,
|
|
138
|
+
) -> None:
|
|
139
|
+
if api_key is None:
|
|
140
|
+
api_key = os.environ.get("JIGSAWSTACK_API_KEY")
|
|
141
|
+
|
|
142
|
+
if api_key is None:
|
|
143
|
+
raise ValueError(
|
|
144
|
+
"The api_key client option must be set either by passing api_key to the client or by setting the JIGSAWSTACK_API_KEY environment variable"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if api_url is None:
|
|
148
|
+
api_url = os.environ.get("JIGSAWSTACK_API_URL")
|
|
149
|
+
if api_url is None:
|
|
150
|
+
api_url = f"https://api.jigsawstack.com/v1"
|
|
151
|
+
|
|
152
|
+
self.api_key = api_key
|
|
153
|
+
self.api_url = api_url
|
|
154
|
+
|
|
155
|
+
self.web = AsyncWeb(
|
|
156
|
+
api_key=api_key,
|
|
157
|
+
api_url=api_url,
|
|
158
|
+
disable_request_logging=disable_request_logging,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
self.geo = AsyncGeo(
|
|
162
|
+
api_key=api_key,
|
|
163
|
+
api_url=api_url,
|
|
164
|
+
disable_request_logging=disable_request_logging,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
self.validate = AsyncValidate(
|
|
168
|
+
api_key=api_key,
|
|
169
|
+
api_url=api_url,
|
|
170
|
+
disable_request_logging=disable_request_logging,
|
|
171
|
+
)
|
|
172
|
+
self.audio = AsyncAudio(
|
|
173
|
+
api_key=api_key,
|
|
174
|
+
api_url=api_url,
|
|
175
|
+
disable_request_logging=disable_request_logging,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
self.vision = AsyncVision(
|
|
179
|
+
api_key=api_key,
|
|
180
|
+
api_url=api_url,
|
|
181
|
+
disable_request_logging=disable_request_logging,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
self.store = AsyncStore(
|
|
185
|
+
api_key=api_key,
|
|
186
|
+
api_url=api_url,
|
|
187
|
+
disable_request_logging=disable_request_logging,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
self.summary = AsyncSummary(
|
|
191
|
+
api_key=api_key,
|
|
192
|
+
api_url=api_url,
|
|
193
|
+
disable_request_logging=disable_request_logging,
|
|
194
|
+
).summarize
|
|
195
|
+
|
|
196
|
+
self.prediction = AsyncPrediction(
|
|
197
|
+
api_key=api_key,
|
|
198
|
+
api_url=api_url,
|
|
199
|
+
disable_request_logging=disable_request_logging,
|
|
200
|
+
).predict
|
|
201
|
+
self.text_to_sql = AsyncSQL(
|
|
202
|
+
api_key=api_key,
|
|
203
|
+
api_url=api_url,
|
|
204
|
+
disable_request_logging=disable_request_logging,
|
|
205
|
+
).text_to_sql
|
|
206
|
+
|
|
207
|
+
self.sentiment = AsyncSentiment(
|
|
208
|
+
api_key=api_key,
|
|
209
|
+
api_url=api_url,
|
|
210
|
+
disable_request_logging=disable_request_logging,
|
|
211
|
+
).analyze
|
|
212
|
+
|
|
213
|
+
self.translate = AsyncTranslate(
|
|
214
|
+
api_key=api_key,
|
|
215
|
+
api_url=api_url,
|
|
216
|
+
disable_request_logging=disable_request_logging,
|
|
217
|
+
).translate
|
|
218
|
+
|
|
219
|
+
self.prompt_engine = AsyncPromptEngine(
|
|
220
|
+
api_key=api_key,
|
|
221
|
+
api_url=api_url,
|
|
222
|
+
disable_request_logging=disable_request_logging,
|
|
223
|
+
)
|
|
224
|
+
self.embedding = AsyncEmbedding(
|
|
225
|
+
api_key=api_key,
|
|
226
|
+
api_url=api_url,
|
|
227
|
+
disable_request_logging=disable_request_logging,
|
|
228
|
+
).execute
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# Create a global instance of the Web class
|
|
232
|
+
__all__ = ["JigsawStack", "Search", "JigsawStackError", "AsyncJigsawStack"]
|
jigsawstack/_client.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import os
|
|
3
|
+
from .audio import Audio
|
|
4
|
+
from .vision import Vision
|
|
5
|
+
from .search import Search
|
|
6
|
+
from .prediction import Prediction
|
|
7
|
+
from .sql import SQL
|
|
8
|
+
from .store import KV, File
|
|
9
|
+
from .translate import Translate
|
|
10
|
+
from .web import Web
|
|
11
|
+
from .sentiment import Sentiment
|
|
12
|
+
from .validate import Validate
|
|
13
|
+
from .summary import Summary
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JigsawStack:
|
|
17
|
+
audio: Audio
|
|
18
|
+
vision: Vision
|
|
19
|
+
prediction: Prediction
|
|
20
|
+
sql: SQL
|
|
21
|
+
file: File
|
|
22
|
+
kv: KV
|
|
23
|
+
translate: Translate
|
|
24
|
+
web: Web
|
|
25
|
+
sentiment: Sentiment
|
|
26
|
+
validate: Validate
|
|
27
|
+
summary: Summary
|
|
28
|
+
search: Search
|
|
29
|
+
api_key: str
|
|
30
|
+
api_url: str
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self, api_key: Union[str, None] = None, api_url: Union[str, None] = None
|
|
34
|
+
) -> None:
|
|
35
|
+
if api_key is None:
|
|
36
|
+
api_key = os.environ.get("JIGSAWSTACK_API_KEY")
|
|
37
|
+
|
|
38
|
+
if api_key is None:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
"The api_key client option must be set either by passing api_key to the client or by setting the JIGSAWSTACK_API_KEY environment variable"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if api_url is None:
|
|
44
|
+
api_url = os.environ.get("JIGSAWSTACK_API_URL")
|
|
45
|
+
if api_url is None:
|
|
46
|
+
api_url = f"https://api.jigsawstack.com/v1"
|
|
47
|
+
|
|
48
|
+
self.api_key = api_key
|
|
49
|
+
self.api_url = api_url
|
|
50
|
+
|
|
51
|
+
self.audio = Audio(api_key=api_key, api_url=api_url)
|
|
52
|
+
self.web = Web(api_key=api_key, api_url=api_url)
|
|
53
|
+
self.search = Search(api_key=api_key, api_url=api_url)
|
|
54
|
+
self.sentiment = Sentiment(api_key=api_key, api_url=api_url)
|
|
55
|
+
self.validate = Validate(api_key=api_key, api_url=api_url)
|
|
56
|
+
self.summary = Summary(api_key=api_key, api_url=api_url)
|
|
57
|
+
self.vision = Vision(api_key=api_key, api_url=api_url)
|
|
58
|
+
self.prediction = Prediction(api_key=api_key, api_url=api_url)
|
|
59
|
+
self.sql = SQL(api_key=api_key, api_url=api_url)
|
|
60
|
+
self.file = File(api_key=api_key, api_url=api_url)
|
|
61
|
+
self.kv = KV(api_key=api_key, api_url=api_url)
|
|
62
|
+
self.translate = Translate(api_key=api_key, api_url=api_url)
|
jigsawstack/_config.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ClientConfig:
|
|
5
|
+
base_url: str
|
|
6
|
+
api_key: str
|
|
7
|
+
disable_request_logging: Union[bool, None] = None
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
api_key: str,
|
|
12
|
+
api_url: str,
|
|
13
|
+
disable_request_logging: Union[bool, None] = None,
|
|
14
|
+
):
|
|
15
|
+
self.api_key = api_key
|
|
16
|
+
self.api_url = api_url
|
|
17
|
+
self.disable_request_logging = disable_request_logging
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
from typing import Any, Dict, Generic, List, Union, cast, TypedDict, AsyncGenerator
|
|
2
|
+
import aiohttp
|
|
3
|
+
from typing_extensions import Literal, TypeVar
|
|
4
|
+
from .exceptions import NoContentError, raise_for_code_and_type
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
RequestVerb = Literal["get", "post", "put", "patch", "delete"]
|
|
8
|
+
|
|
9
|
+
T = TypeVar("T")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AsyncRequestConfig(TypedDict):
|
|
13
|
+
api_url: str
|
|
14
|
+
api_key: str
|
|
15
|
+
disable_request_logging: Union[bool, None] = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AsyncRequest(Generic[T]):
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
config: AsyncRequestConfig,
|
|
22
|
+
path: str,
|
|
23
|
+
params: Union[Dict[Any, Any], List[Dict[Any, Any]]],
|
|
24
|
+
verb: RequestVerb,
|
|
25
|
+
headers: Dict[str, str] = {"Content-Type": "application/json"},
|
|
26
|
+
data: Union[bytes, None] = None,
|
|
27
|
+
stream: Union[bool, None] = False,
|
|
28
|
+
):
|
|
29
|
+
self.path = path
|
|
30
|
+
self.params = params
|
|
31
|
+
self.verb = verb
|
|
32
|
+
self.api_url = config.get("api_url")
|
|
33
|
+
self.api_key = config.get("api_key")
|
|
34
|
+
self.data = data
|
|
35
|
+
self.headers = headers
|
|
36
|
+
self.disable_request_logging = config.get("disable_request_logging")
|
|
37
|
+
self.stream = stream
|
|
38
|
+
|
|
39
|
+
async def perform(self) -> Union[T, None]:
|
|
40
|
+
"""
|
|
41
|
+
Async method to make an HTTP request to the JigsawStack API.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Union[T, None]: A generic type of the Request class or None
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
aiohttp.ClientResponseError: If the request fails
|
|
48
|
+
"""
|
|
49
|
+
async with self.__get_session() as session:
|
|
50
|
+
resp = await self.make_request(session, url=f"{self.api_url}{self.path}")
|
|
51
|
+
|
|
52
|
+
# delete calls do not return a body
|
|
53
|
+
if await resp.text() == "" and resp.status == 200:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
# safety net for non-JSON responses
|
|
57
|
+
content_type = resp.headers.get("content-type", "")
|
|
58
|
+
if "application/json" not in content_type:
|
|
59
|
+
raise_for_code_and_type(
|
|
60
|
+
code=500,
|
|
61
|
+
message="Failed to parse JigsawStack API response. Please try again.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# handle error responses
|
|
65
|
+
if resp.status != 200:
|
|
66
|
+
error = await resp.json()
|
|
67
|
+
raise_for_code_and_type(
|
|
68
|
+
code=resp.status,
|
|
69
|
+
message=error.get("message"),
|
|
70
|
+
err=error.get("error"),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return cast(T, await resp.json())
|
|
74
|
+
|
|
75
|
+
async def perform_file(self) -> Union[aiohttp.ClientResponse, None]:
|
|
76
|
+
"""
|
|
77
|
+
Async method to make an HTTP request and return the raw response.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Union[aiohttp.ClientResponse, None]: The raw response object
|
|
81
|
+
"""
|
|
82
|
+
async with self.__get_session() as session:
|
|
83
|
+
resp = await self.make_request(session, url=f"{self.api_url}{self.path}")
|
|
84
|
+
|
|
85
|
+
# delete calls do not return a body
|
|
86
|
+
if await resp.text() == "" and resp.status == 200:
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
# handle error responses
|
|
90
|
+
if (
|
|
91
|
+
"application/json" not in resp.headers.get("content-type", "")
|
|
92
|
+
and resp.status != 200
|
|
93
|
+
):
|
|
94
|
+
raise_for_code_and_type(
|
|
95
|
+
code=500,
|
|
96
|
+
message="Failed to parse JigsawStack API response. Please try again.",
|
|
97
|
+
error_type="InternalServerError",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if resp.status != 200:
|
|
101
|
+
error = await resp.json()
|
|
102
|
+
raise_for_code_and_type(
|
|
103
|
+
code=resp.status,
|
|
104
|
+
message=error.get("message"),
|
|
105
|
+
err=error.get("error"),
|
|
106
|
+
)
|
|
107
|
+
return resp
|
|
108
|
+
|
|
109
|
+
async def perform_with_content(self) -> T:
|
|
110
|
+
"""
|
|
111
|
+
Perform an async HTTP request and return the response content.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
T: The content of the response
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
NoContentError: If the response content is `None`.
|
|
118
|
+
"""
|
|
119
|
+
resp = await self.perform()
|
|
120
|
+
if resp is None:
|
|
121
|
+
raise NoContentError()
|
|
122
|
+
return resp
|
|
123
|
+
|
|
124
|
+
async def perform_with_content_file(self) -> Union[aiohttp.ClientResponse, None]:
|
|
125
|
+
"""
|
|
126
|
+
Perform an async HTTP request and return the raw response.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Union[aiohttp.ClientResponse, None]: The raw response
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
NoContentError: If the response content is `None`.
|
|
133
|
+
"""
|
|
134
|
+
resp = await self.perform_file()
|
|
135
|
+
if resp is None:
|
|
136
|
+
raise NoContentError()
|
|
137
|
+
return resp
|
|
138
|
+
|
|
139
|
+
def __get_headers(self) -> Dict[str, str]:
|
|
140
|
+
"""
|
|
141
|
+
Prepare HTTP headers for the request.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Dict[str, str]: Configured HTTP Headers
|
|
145
|
+
"""
|
|
146
|
+
h = {
|
|
147
|
+
"Content-Type": "application/json",
|
|
148
|
+
"Accept": "application/json",
|
|
149
|
+
"x-api-key": f"{self.api_key}",
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if self.disable_request_logging:
|
|
153
|
+
h["x-jigsaw-no-request-log"] = "true"
|
|
154
|
+
|
|
155
|
+
_headers = h.copy()
|
|
156
|
+
_headers.update(self.headers)
|
|
157
|
+
|
|
158
|
+
return _headers
|
|
159
|
+
|
|
160
|
+
async def perform_streaming(self) -> AsyncGenerator[Union[T, str], None]:
|
|
161
|
+
"""
|
|
162
|
+
Async method to stream response from JigsawStack API.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
AsyncGenerator[Union[T, str], None]: A generator of response chunks
|
|
166
|
+
"""
|
|
167
|
+
async with self.__get_session() as session:
|
|
168
|
+
resp = await self.make_request(session, url=f"{self.api_url}{self.path}")
|
|
169
|
+
|
|
170
|
+
# delete calls do not return a body
|
|
171
|
+
if await resp.text() == "":
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
if resp.status != 200:
|
|
175
|
+
error = await resp.json()
|
|
176
|
+
raise_for_code_and_type(
|
|
177
|
+
code=resp.status,
|
|
178
|
+
message=error.get("message"),
|
|
179
|
+
err=error.get("error"),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
async for chunk in resp.content.iter_chunked(1024): # 1KB chunks
|
|
183
|
+
if chunk:
|
|
184
|
+
yield await self.__try_parse_data(chunk)
|
|
185
|
+
|
|
186
|
+
async def perform_with_content_streaming(
|
|
187
|
+
self,
|
|
188
|
+
) -> AsyncGenerator[Union[T, str], None]:
|
|
189
|
+
"""
|
|
190
|
+
Perform an async HTTP request and return the response content as a streaming response.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
AsyncGenerator[Union[T, str], None]: Streaming response content
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
NoContentError: If the response content is `None`.
|
|
197
|
+
"""
|
|
198
|
+
resp = await self.perform_streaming()
|
|
199
|
+
if resp is None:
|
|
200
|
+
raise NoContentError()
|
|
201
|
+
return resp
|
|
202
|
+
|
|
203
|
+
async def make_request(
|
|
204
|
+
self, session: aiohttp.ClientSession, url: str
|
|
205
|
+
) -> aiohttp.ClientResponse:
|
|
206
|
+
"""
|
|
207
|
+
Make the actual async HTTP request.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
session (aiohttp.ClientSession): The client session
|
|
211
|
+
url (str): The URL to make the request to
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
aiohttp.ClientResponse: The response object from the request
|
|
215
|
+
"""
|
|
216
|
+
headers = self.__get_headers()
|
|
217
|
+
params = self.params
|
|
218
|
+
verb = self.verb
|
|
219
|
+
data = self.data
|
|
220
|
+
|
|
221
|
+
request_params = None if verb.lower() not in ["get", "delete"] else params
|
|
222
|
+
|
|
223
|
+
try:
|
|
224
|
+
return await session.request(
|
|
225
|
+
verb,
|
|
226
|
+
url,
|
|
227
|
+
params=request_params,
|
|
228
|
+
json=params,
|
|
229
|
+
headers=headers,
|
|
230
|
+
data=data,
|
|
231
|
+
)
|
|
232
|
+
except aiohttp.ClientError as e:
|
|
233
|
+
raise e
|
|
234
|
+
|
|
235
|
+
def __get_session(self) -> aiohttp.ClientSession:
|
|
236
|
+
"""
|
|
237
|
+
Create and return an async client session.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
aiohttp.ClientSession: An async client session
|
|
241
|
+
"""
|
|
242
|
+
return aiohttp.ClientSession()
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
async def __try_parse_data(chunk: bytes) -> Union[T, str]:
|
|
246
|
+
"""
|
|
247
|
+
Attempt to parse a chunk of data as JSON or return as text.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
chunk (bytes): The data chunk to parse
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Union[T, str]: Parsed JSON or raw text
|
|
254
|
+
"""
|
|
255
|
+
if not chunk:
|
|
256
|
+
return chunk
|
|
257
|
+
|
|
258
|
+
# Decode bytes to text
|
|
259
|
+
text = chunk.decode("utf-8")
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
# Try to parse as JSON
|
|
263
|
+
return json.loads(text)
|
|
264
|
+
except json.JSONDecodeError:
|
|
265
|
+
# Return as text if not valid JSON
|
|
266
|
+
return text
|