synapsai-python 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.
- synapsai/__init__.py +16 -0
- synapsai/client.py +628 -0
- synapsai/exceptions.py +64 -0
- synapsai/logging.py +70 -0
- synapsai/processing.py +100 -0
- synapsai/resources/__init__.py +59 -0
- synapsai/resources/audio.py +495 -0
- synapsai/resources/chat.py +199 -0
- synapsai/resources/classifications.py +454 -0
- synapsai/resources/completions.py +162 -0
- synapsai/resources/embeddings.py +207 -0
- synapsai/resources/feature_extraction.py +80 -0
- synapsai/resources/fill_mask.py +88 -0
- synapsai/resources/images.py +560 -0
- synapsai/resources/models.py +80 -0
- synapsai/resources/question_answering.py +257 -0
- synapsai/resources/rerank.py +90 -0
- synapsai/resources/videos.py +303 -0
- synapsai/types/__init__.py +144 -0
- synapsai/types/audio.py +177 -0
- synapsai/types/classifications.py +228 -0
- synapsai/types/common.py +56 -0
- synapsai/types/completion.py +244 -0
- synapsai/types/embeddings.py +68 -0
- synapsai/types/feature_extraction.py +31 -0
- synapsai/types/fill_mask.py +38 -0
- synapsai/types/images.py +172 -0
- synapsai/types/models.py +28 -0
- synapsai/types/question_answering.py +127 -0
- synapsai/types/rerank.py +40 -0
- synapsai/types/videos.py +92 -0
- synapsai/utils.py +32 -0
- synapsai_python-0.1.0.dist-info/METADATA +312 -0
- synapsai_python-0.1.0.dist-info/RECORD +37 -0
- synapsai_python-0.1.0.dist-info/WHEEL +5 -0
- synapsai_python-0.1.0.dist-info/licenses/LICENSE +201 -0
- synapsai_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# Copyright 2026 SynapsAI Technologies Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Classification resource handlers
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import TYPE_CHECKING, Optional, Union, List
|
|
20
|
+
from ..types.classifications import (
|
|
21
|
+
AudioClassificationResponse,
|
|
22
|
+
ImageClassificationResponse,
|
|
23
|
+
TextClassificationResponse,
|
|
24
|
+
TokenClassificationResponse,
|
|
25
|
+
VideoClassificationResponse,
|
|
26
|
+
VideoClassificationResponse,
|
|
27
|
+
ZeroShotAudioClassificationResponse,
|
|
28
|
+
ZeroShotClassificationResponse,
|
|
29
|
+
ZeroShotImageClassificationResponse,
|
|
30
|
+
ZeroShotObjectDetectionResponse,
|
|
31
|
+
)
|
|
32
|
+
from PIL import Image
|
|
33
|
+
import numpy as np
|
|
34
|
+
from ..processing import process_image_input, process_audio_input, process_video_input
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from ..client import SynapsAI, AsyncSynapsAI
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ZeroShotClassificationsResource:
|
|
43
|
+
"""Zero-Shot Classification resource handler"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, client: "SynapsAI"):
|
|
46
|
+
self._client = client
|
|
47
|
+
|
|
48
|
+
def audio(
|
|
49
|
+
self,
|
|
50
|
+
model: str,
|
|
51
|
+
audios,
|
|
52
|
+
candidate_labels: list[str],
|
|
53
|
+
hypothesis_template: Optional[str] = None,
|
|
54
|
+
) -> ZeroShotAudioClassificationResponse:
|
|
55
|
+
"""Assign labels to the audio(s) passed as inputs (zero-shot)."""
|
|
56
|
+
|
|
57
|
+
audios = process_audio_input(audios)
|
|
58
|
+
|
|
59
|
+
request_data = self._client._build_request(
|
|
60
|
+
model=model,
|
|
61
|
+
audios=audios,
|
|
62
|
+
candidate_labels=candidate_labels,
|
|
63
|
+
hypothesis_template=hypothesis_template,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
endpoint = "classifications/zero-shot/audio"
|
|
67
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
68
|
+
return ZeroShotAudioClassificationResponse.model_validate(response.json())
|
|
69
|
+
|
|
70
|
+
def text(
|
|
71
|
+
self,
|
|
72
|
+
model: str,
|
|
73
|
+
sequences: Union[str, List[str]],
|
|
74
|
+
candidate_labels: Union[str, List[str]],
|
|
75
|
+
hypothesis_template: Optional[str] = None,
|
|
76
|
+
multi_label: Optional[bool] = None,
|
|
77
|
+
) -> ZeroShotClassificationResponse:
|
|
78
|
+
"""Zero-shot text classification."""
|
|
79
|
+
|
|
80
|
+
request_data = self._client._build_request(
|
|
81
|
+
model=model,
|
|
82
|
+
sequences=sequences,
|
|
83
|
+
candidate_labels=candidate_labels,
|
|
84
|
+
hypothesis_template=hypothesis_template,
|
|
85
|
+
multi_label=multi_label,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
endpoint = "classifications/zero-shot"
|
|
89
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
90
|
+
return ZeroShotClassificationResponse.model_validate(response.json())
|
|
91
|
+
|
|
92
|
+
def image(
|
|
93
|
+
self,
|
|
94
|
+
model: str,
|
|
95
|
+
image: Union[str, List[str], Image.Image, List[Image.Image]],
|
|
96
|
+
candidate_labels: list[str],
|
|
97
|
+
hypothesis_template: Optional[str] = None,
|
|
98
|
+
timeout: Optional[float] = None,
|
|
99
|
+
) -> ZeroShotImageClassificationResponse:
|
|
100
|
+
"""Zero-shot image classification."""
|
|
101
|
+
|
|
102
|
+
image = process_image_input(image)
|
|
103
|
+
|
|
104
|
+
request_data = self._client._build_request(
|
|
105
|
+
model=model,
|
|
106
|
+
image=image,
|
|
107
|
+
candidate_labels=candidate_labels,
|
|
108
|
+
hypothesis_template=hypothesis_template,
|
|
109
|
+
timeout=timeout,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
endpoint = "classifications/zero-shot/image"
|
|
113
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
114
|
+
return ZeroShotImageClassificationResponse.model_validate(response.json())
|
|
115
|
+
|
|
116
|
+
class ClassificationsResource:
|
|
117
|
+
"""Classification resource handler"""
|
|
118
|
+
|
|
119
|
+
def __init__(self, client: "SynapsAI"):
|
|
120
|
+
self._client = client
|
|
121
|
+
self.zero_shot = ZeroShotClassificationsResource(client)
|
|
122
|
+
|
|
123
|
+
def audio(
|
|
124
|
+
self,
|
|
125
|
+
model: str,
|
|
126
|
+
inputs: Union[np.ndarray, bytes, dict],
|
|
127
|
+
top_k: Optional[int] = None,
|
|
128
|
+
function_to_apply: Optional[str] = None,
|
|
129
|
+
) -> AudioClassificationResponse:
|
|
130
|
+
"""Audio classification."""
|
|
131
|
+
|
|
132
|
+
inputs = process_audio_input(inputs)
|
|
133
|
+
|
|
134
|
+
# Build request
|
|
135
|
+
request_data = self._client._build_request(
|
|
136
|
+
model=model,
|
|
137
|
+
inputs=inputs,
|
|
138
|
+
top_k=top_k,
|
|
139
|
+
function_to_apply=function_to_apply
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
endpoint = "classifications/audio"
|
|
143
|
+
|
|
144
|
+
# Make request
|
|
145
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
146
|
+
response_data = response.json()
|
|
147
|
+
return AudioClassificationResponse(**response_data)
|
|
148
|
+
|
|
149
|
+
def image(
|
|
150
|
+
self,
|
|
151
|
+
model: str,
|
|
152
|
+
inputs: Union[str, list[str], Image.Image, list[Image.Image]],
|
|
153
|
+
function_to_apply: Optional[str] = None,
|
|
154
|
+
top_k: Optional[int] = None,
|
|
155
|
+
timeout: Optional[float] = None,
|
|
156
|
+
) -> ImageClassificationResponse:
|
|
157
|
+
"""Assign labels to the image(s) passed as inputs."""
|
|
158
|
+
|
|
159
|
+
inputs = process_image_input(inputs)
|
|
160
|
+
|
|
161
|
+
# Build request
|
|
162
|
+
request_data = self._client._build_request(
|
|
163
|
+
model=model,
|
|
164
|
+
inputs=inputs,
|
|
165
|
+
function_to_apply=function_to_apply,
|
|
166
|
+
top_k=top_k,
|
|
167
|
+
timeout=timeout
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
endpoint = "classifications/image"
|
|
171
|
+
|
|
172
|
+
# Make request
|
|
173
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
174
|
+
response_data = response.json()
|
|
175
|
+
return ImageClassificationResponse.model_validate(response_data)
|
|
176
|
+
|
|
177
|
+
def text(
|
|
178
|
+
self,
|
|
179
|
+
model: str,
|
|
180
|
+
inputs: Union[str, list[str], dict[str], list[dict[str]]],
|
|
181
|
+
top_k: Optional[int] = None,
|
|
182
|
+
function_to_apply: Optional[str] = None,
|
|
183
|
+
) -> TextClassificationResponse:
|
|
184
|
+
"""Classify the text(s) given as inputs."""
|
|
185
|
+
|
|
186
|
+
# Build request
|
|
187
|
+
request_data = self._client._build_request(
|
|
188
|
+
model=model,
|
|
189
|
+
inputs=inputs,
|
|
190
|
+
top_k=top_k,
|
|
191
|
+
function_to_apply=function_to_apply
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
endpoint = "classifications/text"
|
|
195
|
+
|
|
196
|
+
# Make request
|
|
197
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
198
|
+
response_data = response.json()
|
|
199
|
+
return TextClassificationResponse.model_validate(response_data)
|
|
200
|
+
|
|
201
|
+
def token(
|
|
202
|
+
self,
|
|
203
|
+
model: str,
|
|
204
|
+
inputs: Union[str, List[str]],
|
|
205
|
+
) -> TokenClassificationResponse:
|
|
206
|
+
"""Classify each token of the text(s) given as inputs."""
|
|
207
|
+
|
|
208
|
+
# Build request
|
|
209
|
+
request_data = self._client._build_request(
|
|
210
|
+
model=model,
|
|
211
|
+
inputs=inputs
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
endpoint = "classifications/token"
|
|
215
|
+
|
|
216
|
+
# Make request
|
|
217
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
218
|
+
response_data = response.json()
|
|
219
|
+
return TokenClassificationResponse.model_validate(response_data)
|
|
220
|
+
|
|
221
|
+
def video(
|
|
222
|
+
self,
|
|
223
|
+
model: str,
|
|
224
|
+
inputs: Union[str, list[str]],
|
|
225
|
+
top_k: Optional[int] = None,
|
|
226
|
+
num_frames: Optional[int] = None,
|
|
227
|
+
frame_sampling_rate: Optional[int] = None,
|
|
228
|
+
function_to_apply: Optional[str] = None,
|
|
229
|
+
) -> VideoClassificationResponse:
|
|
230
|
+
"""Assign labels to the video(s) passed as inputs."""
|
|
231
|
+
|
|
232
|
+
inputs = process_video_input(inputs)
|
|
233
|
+
|
|
234
|
+
# Build request
|
|
235
|
+
request_data = self._client._build_request(
|
|
236
|
+
model=model,
|
|
237
|
+
inputs=inputs,
|
|
238
|
+
top_k=top_k,
|
|
239
|
+
num_frames=num_frames,
|
|
240
|
+
frame_sampling_rate=frame_sampling_rate,
|
|
241
|
+
function_to_apply=function_to_apply
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
endpoint = "classifications/video"
|
|
245
|
+
|
|
246
|
+
# Make request
|
|
247
|
+
response = self._client._post(endpoint, json_data=request_data)
|
|
248
|
+
response_data = response.json()
|
|
249
|
+
return VideoClassificationResponse.model_validate(response_data)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class AsyncZeroShotClassificationsResource:
|
|
254
|
+
"""Async Zero-Shot Classification resource handler"""
|
|
255
|
+
|
|
256
|
+
def __init__(self, client: "AsyncSynapsAI"):
|
|
257
|
+
self._client = client
|
|
258
|
+
|
|
259
|
+
async def audio(
|
|
260
|
+
self,
|
|
261
|
+
model: str,
|
|
262
|
+
audios,
|
|
263
|
+
candidate_labels: list[str],
|
|
264
|
+
hypothesis_template: Optional[str] = None,
|
|
265
|
+
) -> ZeroShotAudioClassificationResponse:
|
|
266
|
+
"""Assign labels to the audio(s) passed as inputs (zero-shot)."""
|
|
267
|
+
|
|
268
|
+
request_data = self._client._build_request(
|
|
269
|
+
model=model,
|
|
270
|
+
audios=audios,
|
|
271
|
+
candidate_labels=candidate_labels,
|
|
272
|
+
hypothesis_template=hypothesis_template,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
endpoint = "classifications/zero-shot/audio"
|
|
276
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
277
|
+
return ZeroShotAudioClassificationResponse.model_validate(response.json())
|
|
278
|
+
|
|
279
|
+
async def text(
|
|
280
|
+
self,
|
|
281
|
+
model: str,
|
|
282
|
+
sequences: Union[str, List[str]],
|
|
283
|
+
candidate_labels: Union[str, List[str]],
|
|
284
|
+
hypothesis_template: Optional[str] = None,
|
|
285
|
+
multi_label: Optional[bool] = None,
|
|
286
|
+
) -> ZeroShotClassificationResponse:
|
|
287
|
+
"""Zero-shot text classification."""
|
|
288
|
+
|
|
289
|
+
request_data = self._client._build_request(
|
|
290
|
+
model=model,
|
|
291
|
+
sequences=sequences,
|
|
292
|
+
candidate_labels=candidate_labels,
|
|
293
|
+
hypothesis_template=hypothesis_template,
|
|
294
|
+
multi_label=multi_label,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
endpoint = "classifications/zero-shot"
|
|
298
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
299
|
+
return ZeroShotClassificationResponse.model_validate(response.json())
|
|
300
|
+
|
|
301
|
+
async def image(
|
|
302
|
+
self,
|
|
303
|
+
model: str,
|
|
304
|
+
image: Union[str, List[str], Image.Image, List[Image.Image]],
|
|
305
|
+
candidate_labels: list[str],
|
|
306
|
+
hypothesis_template: Optional[str] = None,
|
|
307
|
+
timeout: Optional[float] = None,
|
|
308
|
+
) -> ZeroShotImageClassificationResponse:
|
|
309
|
+
"""Zero-shot image classification."""
|
|
310
|
+
|
|
311
|
+
request_data = self._client._build_request(
|
|
312
|
+
model=model,
|
|
313
|
+
image=image,
|
|
314
|
+
candidate_labels=candidate_labels,
|
|
315
|
+
hypothesis_template=hypothesis_template,
|
|
316
|
+
timeout=timeout,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
endpoint = "classifications/zero-shot/image"
|
|
320
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
321
|
+
return ZeroShotImageClassificationResponse.model_validate(response.json())
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class AsyncClassificationsResource:
|
|
325
|
+
"""Async Classification resource handler"""
|
|
326
|
+
|
|
327
|
+
def __init__(self, client: "AsyncSynapsAI"):
|
|
328
|
+
self._client = client
|
|
329
|
+
self.zero_shot = AsyncZeroShotClassificationsResource(client)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
async def audio(
|
|
333
|
+
self,
|
|
334
|
+
model: str,
|
|
335
|
+
inputs: Union[np.ndarray, bytes, dict],
|
|
336
|
+
top_k: Optional[int] = None,
|
|
337
|
+
function_to_apply: Optional[str] = None,
|
|
338
|
+
) -> AudioClassificationResponse:
|
|
339
|
+
"""Audio classification."""
|
|
340
|
+
|
|
341
|
+
# Build request
|
|
342
|
+
request_data = self._client._build_request(
|
|
343
|
+
model=model,
|
|
344
|
+
inputs=inputs,
|
|
345
|
+
top_k=top_k,
|
|
346
|
+
function_to_apply=function_to_apply
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
endpoint = "classifications/audio"
|
|
350
|
+
|
|
351
|
+
# Make request
|
|
352
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
353
|
+
response_data = response.json()
|
|
354
|
+
return AudioClassificationResponse.model_validate(response_data)
|
|
355
|
+
|
|
356
|
+
async def image(
|
|
357
|
+
self,
|
|
358
|
+
model: str,
|
|
359
|
+
inputs: Union[str, list[str], Image.Image, list[Image.Image]],
|
|
360
|
+
function_to_apply: Optional[str] = None,
|
|
361
|
+
top_k: Optional[int] = None,
|
|
362
|
+
timeout: Optional[float] = None,
|
|
363
|
+
) -> ImageClassificationResponse:
|
|
364
|
+
"""Assign labels to the image(s) passed as inputs."""
|
|
365
|
+
|
|
366
|
+
# Build request
|
|
367
|
+
request_data = self._client._build_request(
|
|
368
|
+
model=model,
|
|
369
|
+
inputs=inputs,
|
|
370
|
+
function_to_apply=function_to_apply,
|
|
371
|
+
top_k=top_k,
|
|
372
|
+
timeout=timeout
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
endpoint = "classifications/image"
|
|
376
|
+
|
|
377
|
+
# Make request
|
|
378
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
379
|
+
response_data = response.json()
|
|
380
|
+
return ImageClassificationResponse.model_validate(response_data)
|
|
381
|
+
|
|
382
|
+
async def text(
|
|
383
|
+
self,
|
|
384
|
+
model: str,
|
|
385
|
+
inputs: Union[str, list[str], dict[str], list[dict[str]]],
|
|
386
|
+
top_k: Optional[int] = None,
|
|
387
|
+
function_to_apply: Optional[str] = None,
|
|
388
|
+
) -> TextClassificationResponse:
|
|
389
|
+
"""Classify the text(s) given as inputs."""
|
|
390
|
+
|
|
391
|
+
# Build request
|
|
392
|
+
request_data = self._client._build_request(
|
|
393
|
+
model=model,
|
|
394
|
+
inputs=inputs,
|
|
395
|
+
top_k=top_k,
|
|
396
|
+
function_to_apply=function_to_apply
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
endpoint = "classifications/text"
|
|
400
|
+
|
|
401
|
+
# Make request
|
|
402
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
403
|
+
response_data = response.json()
|
|
404
|
+
return TextClassificationResponse.model_validate(response_data)
|
|
405
|
+
|
|
406
|
+
async def token(
|
|
407
|
+
self,
|
|
408
|
+
model: str,
|
|
409
|
+
inputs: Union[str, List[str]],
|
|
410
|
+
) -> TokenClassificationResponse:
|
|
411
|
+
"""Classify each token of the text(s) given as inputs."""
|
|
412
|
+
|
|
413
|
+
# Build request
|
|
414
|
+
request_data = self._client._build_request(
|
|
415
|
+
model=model,
|
|
416
|
+
inputs=inputs
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
endpoint = "classifications/token"
|
|
420
|
+
|
|
421
|
+
# Make request
|
|
422
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
423
|
+
response_data = response.json()
|
|
424
|
+
return TokenClassificationResponse.model_validate(response_data)
|
|
425
|
+
|
|
426
|
+
async def video(
|
|
427
|
+
self,
|
|
428
|
+
model: str,
|
|
429
|
+
inputs: Union[str, list[str]],
|
|
430
|
+
top_k: Optional[int] = None,
|
|
431
|
+
num_frames: Optional[int] = None,
|
|
432
|
+
frame_sampling_rate: Optional[int] = None,
|
|
433
|
+
function_to_apply: Optional[str] = None,
|
|
434
|
+
) -> VideoClassificationResponse:
|
|
435
|
+
"""Assign labels to the video(s) passed as inputs."""
|
|
436
|
+
|
|
437
|
+
inputs = process_video_input(inputs)
|
|
438
|
+
|
|
439
|
+
# Build request
|
|
440
|
+
request_data = self._client._build_request(
|
|
441
|
+
model=model,
|
|
442
|
+
inputs=inputs,
|
|
443
|
+
top_k=top_k,
|
|
444
|
+
num_frames=num_frames,
|
|
445
|
+
frame_sampling_rate=frame_sampling_rate,
|
|
446
|
+
function_to_apply=function_to_apply
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
endpoint = "classifications/video"
|
|
450
|
+
|
|
451
|
+
# Make request
|
|
452
|
+
response = await self._client._post(endpoint, json_data=request_data)
|
|
453
|
+
response_data = response.json()
|
|
454
|
+
return VideoClassificationResponse.model_validate(response_data)
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Copyright 2026 SynapsAI Technologies Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Chat completion resource handlers
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import Union, Iterator, AsyncIterator, TYPE_CHECKING, Optional, Dict
|
|
20
|
+
import json
|
|
21
|
+
|
|
22
|
+
from ..types.completion import (
|
|
23
|
+
CompletionResponse,
|
|
24
|
+
CompletionChunk,
|
|
25
|
+
)
|
|
26
|
+
from ..logging import get_logger
|
|
27
|
+
from ..exceptions import APIError
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from ..client import SynapsAI, AsyncSynapsAI
|
|
31
|
+
|
|
32
|
+
logger = get_logger(__name__)
|
|
33
|
+
|
|
34
|
+
class CompletionsResource:
|
|
35
|
+
"""Chat completions resource"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, client: "SynapsAI"):
|
|
38
|
+
self._client = client
|
|
39
|
+
|
|
40
|
+
def create(
|
|
41
|
+
self,
|
|
42
|
+
model: str,
|
|
43
|
+
prompt: str,
|
|
44
|
+
temperature: float = 1.0,
|
|
45
|
+
top_p: float = 1.0,
|
|
46
|
+
n: int = 1,
|
|
47
|
+
stream: bool = False,
|
|
48
|
+
stop = [],
|
|
49
|
+
max_completion_tokens = 128,
|
|
50
|
+
presence_penalty: float = 0.0,
|
|
51
|
+
frequency_penalty: float = 0.0,
|
|
52
|
+
logit_bias: Optional[Dict[int, float]] = None,
|
|
53
|
+
**kwargs
|
|
54
|
+
) -> Union[CompletionResponse, Iterator[CompletionChunk]]:
|
|
55
|
+
"""Create a chat completion"""
|
|
56
|
+
|
|
57
|
+
# Build request
|
|
58
|
+
request_data = self._client._build_request(
|
|
59
|
+
model=model,
|
|
60
|
+
prompt=prompt,
|
|
61
|
+
temperature=temperature,
|
|
62
|
+
top_p=top_p,
|
|
63
|
+
n=n,
|
|
64
|
+
stream=stream,
|
|
65
|
+
stop=stop,
|
|
66
|
+
max_completion_tokens=max_completion_tokens,
|
|
67
|
+
presence_penalty=presence_penalty,
|
|
68
|
+
frequency_penalty=frequency_penalty,
|
|
69
|
+
logit_bias=logit_bias,
|
|
70
|
+
**kwargs
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
endpoint = "completions"
|
|
74
|
+
|
|
75
|
+
if stream:
|
|
76
|
+
return self._stream_completions(endpoint, request_data)
|
|
77
|
+
else:
|
|
78
|
+
response = self._client._post(endpoint, request_data)
|
|
79
|
+
return CompletionResponse.model_validate(response.json())
|
|
80
|
+
|
|
81
|
+
def _stream_completions(self, endpoint, request_data) -> Iterator[CompletionChunk]:
|
|
82
|
+
for chunk_data in self._client._stream_response(endpoint, request_data):
|
|
83
|
+
try:
|
|
84
|
+
error = chunk_data.get("error")
|
|
85
|
+
if error:
|
|
86
|
+
raise APIError(error)
|
|
87
|
+
yield CompletionChunk(**chunk_data)
|
|
88
|
+
except APIError as e:
|
|
89
|
+
raise e
|
|
90
|
+
except Exception as e:
|
|
91
|
+
print(e)
|
|
92
|
+
logger.warning(
|
|
93
|
+
"Failed to parse CompletionChunk",
|
|
94
|
+
exc_info=True,
|
|
95
|
+
extra={"endpoint": endpoint},
|
|
96
|
+
)
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class AsyncCompletionsResource:
|
|
103
|
+
"""Async chat completions resource"""
|
|
104
|
+
|
|
105
|
+
def __init__(self, client: "AsyncSynapsAI"):
|
|
106
|
+
self._client = client
|
|
107
|
+
|
|
108
|
+
async def create(
|
|
109
|
+
self,
|
|
110
|
+
model: str,
|
|
111
|
+
prompt: str,
|
|
112
|
+
temperature: float = 1.0,
|
|
113
|
+
top_p: float = 1.0,
|
|
114
|
+
n: int = 1,
|
|
115
|
+
stream: bool = False,
|
|
116
|
+
stop = [],
|
|
117
|
+
max_completion_tokens = 128,
|
|
118
|
+
presence_penalty: float = 0.0,
|
|
119
|
+
frequency_penalty: float = 0.0,
|
|
120
|
+
logit_bias = None,
|
|
121
|
+
**kwargs
|
|
122
|
+
) -> Union[CompletionResponse, AsyncIterator[CompletionChunk]]:
|
|
123
|
+
"""Create a chat completion asynchronously"""
|
|
124
|
+
|
|
125
|
+
# Build request
|
|
126
|
+
request_data = self._client._build_request(
|
|
127
|
+
model=model,
|
|
128
|
+
prompt=prompt,
|
|
129
|
+
temperature=temperature,
|
|
130
|
+
top_p=top_p,
|
|
131
|
+
n=n,
|
|
132
|
+
stream=stream,
|
|
133
|
+
stop=stop,
|
|
134
|
+
max_completion_tokens=max_completion_tokens,
|
|
135
|
+
presence_penalty=presence_penalty,
|
|
136
|
+
frequency_penalty=frequency_penalty,
|
|
137
|
+
logit_bias=logit_bias,
|
|
138
|
+
**kwargs
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
endpoint = "completions"
|
|
142
|
+
|
|
143
|
+
# Make request
|
|
144
|
+
if stream:
|
|
145
|
+
return self._stream_completions(endpoint, request_data)
|
|
146
|
+
else:
|
|
147
|
+
response = await self._client._post(endpoint, request_data)
|
|
148
|
+
return CompletionResponse.model_validate(response.json())
|
|
149
|
+
|
|
150
|
+
async def _stream_completions(self, endpoint, request_data) -> AsyncIterator[CompletionChunk]:
|
|
151
|
+
async for chunk_data in self._client._stream_response(endpoint, request_data):
|
|
152
|
+
try:
|
|
153
|
+
yield CompletionChunk(**chunk_data)
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.warning(
|
|
156
|
+
"Failed to parse CompletionChunk",
|
|
157
|
+
exc_info=True,
|
|
158
|
+
extra={"endpoint": endpoint},
|
|
159
|
+
)
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
|