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.
@@ -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