sarvamai 0.1.5a6__py3-none-any.whl → 0.1.5a8__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.
Files changed (93) hide show
  1. sarvamai/__init__.py +45 -1
  2. sarvamai/chat/__init__.py +2 -0
  3. sarvamai/chat/client.py +9 -10
  4. sarvamai/chat/raw_client.py +48 -37
  5. sarvamai/client.py +13 -31
  6. sarvamai/core/__init__.py +5 -0
  7. sarvamai/core/api_error.py +13 -5
  8. sarvamai/core/client_wrapper.py +17 -20
  9. sarvamai/core/events.py +30 -0
  10. sarvamai/core/force_multipart.py +16 -0
  11. sarvamai/core/http_client.py +70 -24
  12. sarvamai/core/http_response.py +19 -11
  13. sarvamai/core/pydantic_utilities.py +69 -108
  14. sarvamai/environment.py +10 -3
  15. sarvamai/errors/__init__.py +2 -0
  16. sarvamai/errors/bad_request_error.py +4 -3
  17. sarvamai/errors/forbidden_error.py +4 -3
  18. sarvamai/errors/internal_server_error.py +4 -3
  19. sarvamai/errors/service_unavailable_error.py +4 -2
  20. sarvamai/errors/too_many_requests_error.py +4 -3
  21. sarvamai/errors/unprocessable_entity_error.py +4 -3
  22. sarvamai/requests/__init__.py +18 -0
  23. sarvamai/requests/audio_data.py +21 -0
  24. sarvamai/requests/audio_message.py +8 -0
  25. sarvamai/requests/chat_completion_request_message.py +3 -1
  26. sarvamai/requests/create_chat_completion_response.py +2 -2
  27. sarvamai/requests/diarized_transcript.py +2 -1
  28. sarvamai/requests/error_data.py +15 -0
  29. sarvamai/requests/error_details.py +0 -1
  30. sarvamai/requests/events_data.py +17 -0
  31. sarvamai/requests/language_identification_response.py +0 -1
  32. sarvamai/requests/speech_to_text_response.py +1 -2
  33. sarvamai/requests/speech_to_text_streaming_response.py +10 -0
  34. sarvamai/requests/speech_to_text_streaming_response_data.py +9 -0
  35. sarvamai/requests/speech_to_text_translate_response.py +0 -1
  36. sarvamai/requests/text_to_speech_response.py +2 -2
  37. sarvamai/requests/timestamps_model.py +2 -1
  38. sarvamai/requests/transcription_data.py +35 -0
  39. sarvamai/requests/transcription_metrics.py +15 -0
  40. sarvamai/requests/translation_response.py +0 -1
  41. sarvamai/requests/transliteration_response.py +0 -1
  42. sarvamai/speech_to_text/__init__.py +2 -0
  43. sarvamai/speech_to_text/client.py +13 -14
  44. sarvamai/speech_to_text/raw_client.py +99 -68
  45. sarvamai/speech_to_text_streaming/__init__.py +7 -0
  46. sarvamai/speech_to_text_streaming/client.py +189 -0
  47. sarvamai/speech_to_text_streaming/raw_client.py +166 -0
  48. sarvamai/speech_to_text_streaming/socket_client.py +133 -0
  49. sarvamai/speech_to_text_streaming/types/__init__.py +8 -0
  50. sarvamai/speech_to_text_streaming/types/speech_to_text_streaming_language_code.py +8 -0
  51. sarvamai/speech_to_text_streaming/types/speech_to_text_streaming_model.py +5 -0
  52. sarvamai/text/__init__.py +2 -0
  53. sarvamai/text/client.py +24 -25
  54. sarvamai/text/raw_client.py +126 -91
  55. sarvamai/text_to_speech/__init__.py +2 -0
  56. sarvamai/text_to_speech/client.py +10 -11
  57. sarvamai/text_to_speech/raw_client.py +49 -38
  58. sarvamai/types/__init__.py +24 -0
  59. sarvamai/types/audio_data.py +33 -0
  60. sarvamai/types/audio_data_encoding.py +5 -0
  61. sarvamai/types/audio_message.py +20 -0
  62. sarvamai/types/chat_completion_request_assistant_message.py +3 -3
  63. sarvamai/types/chat_completion_request_message.py +3 -2
  64. sarvamai/types/chat_completion_request_system_message.py +3 -3
  65. sarvamai/types/chat_completion_request_user_message.py +3 -3
  66. sarvamai/types/chat_completion_response_message.py +3 -3
  67. sarvamai/types/choice.py +4 -4
  68. sarvamai/types/completion_usage.py +3 -3
  69. sarvamai/types/create_chat_completion_response.py +3 -3
  70. sarvamai/types/diarized_entry.py +3 -3
  71. sarvamai/types/diarized_transcript.py +3 -3
  72. sarvamai/types/error_data.py +27 -0
  73. sarvamai/types/error_details.py +3 -3
  74. sarvamai/types/error_message.py +4 -4
  75. sarvamai/types/events_data.py +28 -0
  76. sarvamai/types/format.py +5 -0
  77. sarvamai/types/language_identification_response.py +2 -2
  78. sarvamai/types/speech_to_text_response.py +3 -3
  79. sarvamai/types/speech_to_text_streaming_response.py +22 -0
  80. sarvamai/types/speech_to_text_streaming_response_data.py +9 -0
  81. sarvamai/types/speech_to_text_streaming_response_type.py +5 -0
  82. sarvamai/types/speech_to_text_translate_response.py +3 -3
  83. sarvamai/types/text_to_speech_response.py +2 -2
  84. sarvamai/types/timestamps_model.py +2 -2
  85. sarvamai/types/transcription_data.py +45 -0
  86. sarvamai/types/transcription_metrics.py +27 -0
  87. sarvamai/types/translation_response.py +2 -2
  88. sarvamai/types/transliteration_response.py +2 -2
  89. sarvamai-0.1.5a8.dist-info/METADATA +28 -0
  90. sarvamai-0.1.5a8.dist-info/RECORD +134 -0
  91. sarvamai-0.1.5a6.dist-info/METADATA +0 -174
  92. sarvamai-0.1.5a6.dist-info/RECORD +0 -106
  93. {sarvamai-0.1.5a6.dist-info → sarvamai-0.1.5a8.dist-info}/WHEEL +0 -0
@@ -0,0 +1,16 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+
4
+ class ForceMultipartDict(dict):
5
+ """
6
+ A dictionary subclass that always evaluates to True in boolean contexts.
7
+
8
+ This is used to force multipart/form-data encoding in HTTP requests even when
9
+ the dictionary is empty, which would normally evaluate to False.
10
+ """
11
+
12
+ def __bool__(self):
13
+ return True
14
+
15
+
16
+ FORCE_MULTIPART = ForceMultipartDict()
@@ -11,10 +11,12 @@ from random import random
11
11
 
12
12
  import httpx
13
13
  from .file import File, convert_file_dict_to_httpx_tuples
14
+ from .force_multipart import FORCE_MULTIPART
14
15
  from .jsonable_encoder import jsonable_encoder
15
16
  from .query_encoder import encode_query
16
17
  from .remove_none_from_dict import remove_none_from_dict
17
18
  from .request_options import RequestOptions
19
+ from httpx._types import RequestFiles
18
20
 
19
21
  INITIAL_RETRY_DELAY_SECONDS = 0.5
20
22
  MAX_RETRY_DELAY_SECONDS = 10
@@ -178,11 +180,17 @@ class HttpClient:
178
180
  json: typing.Optional[typing.Any] = None,
179
181
  data: typing.Optional[typing.Any] = None,
180
182
  content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None,
181
- files: typing.Optional[typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]] = None,
183
+ files: typing.Optional[
184
+ typing.Union[
185
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]],
186
+ typing.List[typing.Tuple[str, File]],
187
+ ]
188
+ ] = None,
182
189
  headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
183
190
  request_options: typing.Optional[RequestOptions] = None,
184
191
  retries: int = 2,
185
192
  omit: typing.Optional[typing.Any] = None,
193
+ force_multipart: typing.Optional[bool] = None,
186
194
  ) -> httpx.Response:
187
195
  base_url = self.get_base_url(base_url)
188
196
  timeout = (
@@ -193,6 +201,15 @@ class HttpClient:
193
201
 
194
202
  json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
195
203
 
204
+ request_files: typing.Optional[RequestFiles] = (
205
+ convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
206
+ if (files is not None and files is not omit and isinstance(files, dict))
207
+ else None
208
+ )
209
+
210
+ if (request_files is None or len(request_files) == 0) and force_multipart:
211
+ request_files = FORCE_MULTIPART
212
+
196
213
  response = self.httpx_client.request(
197
214
  method=method,
198
215
  url=urllib.parse.urljoin(f"{base_url}/", path),
@@ -225,11 +242,7 @@ class HttpClient:
225
242
  json=json_body,
226
243
  data=data_body,
227
244
  content=content,
228
- files=(
229
- convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
230
- if (files is not None and files is not omit)
231
- else None
232
- ),
245
+ files=request_files,
233
246
  timeout=timeout,
234
247
  )
235
248
 
@@ -264,11 +277,17 @@ class HttpClient:
264
277
  json: typing.Optional[typing.Any] = None,
265
278
  data: typing.Optional[typing.Any] = None,
266
279
  content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None,
267
- files: typing.Optional[typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]] = None,
280
+ files: typing.Optional[
281
+ typing.Union[
282
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]],
283
+ typing.List[typing.Tuple[str, File]],
284
+ ]
285
+ ] = None,
268
286
  headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
269
287
  request_options: typing.Optional[RequestOptions] = None,
270
288
  retries: int = 2,
271
289
  omit: typing.Optional[typing.Any] = None,
290
+ force_multipart: typing.Optional[bool] = None,
272
291
  ) -> typing.Iterator[httpx.Response]:
273
292
  base_url = self.get_base_url(base_url)
274
293
  timeout = (
@@ -277,6 +296,15 @@ class HttpClient:
277
296
  else self.base_timeout()
278
297
  )
279
298
 
299
+ request_files: typing.Optional[RequestFiles] = (
300
+ convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
301
+ if (files is not None and files is not omit and isinstance(files, dict))
302
+ else None
303
+ )
304
+
305
+ if (request_files is None or len(request_files) == 0) and force_multipart:
306
+ request_files = FORCE_MULTIPART
307
+
280
308
  json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
281
309
 
282
310
  with self.httpx_client.stream(
@@ -311,11 +339,7 @@ class HttpClient:
311
339
  json=json_body,
312
340
  data=data_body,
313
341
  content=content,
314
- files=(
315
- convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
316
- if (files is not None and files is not omit)
317
- else None
318
- ),
342
+ files=request_files,
319
343
  timeout=timeout,
320
344
  ) as stream:
321
345
  yield stream
@@ -354,11 +378,17 @@ class AsyncHttpClient:
354
378
  json: typing.Optional[typing.Any] = None,
355
379
  data: typing.Optional[typing.Any] = None,
356
380
  content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None,
357
- files: typing.Optional[typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]] = None,
381
+ files: typing.Optional[
382
+ typing.Union[
383
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]],
384
+ typing.List[typing.Tuple[str, File]],
385
+ ]
386
+ ] = None,
358
387
  headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
359
388
  request_options: typing.Optional[RequestOptions] = None,
360
389
  retries: int = 2,
361
390
  omit: typing.Optional[typing.Any] = None,
391
+ force_multipart: typing.Optional[bool] = None,
362
392
  ) -> httpx.Response:
363
393
  base_url = self.get_base_url(base_url)
364
394
  timeout = (
@@ -367,6 +397,15 @@ class AsyncHttpClient:
367
397
  else self.base_timeout()
368
398
  )
369
399
 
400
+ request_files: typing.Optional[RequestFiles] = (
401
+ convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
402
+ if (files is not None and files is not omit and isinstance(files, dict))
403
+ else None
404
+ )
405
+
406
+ if (request_files is None or len(request_files) == 0) and force_multipart:
407
+ request_files = FORCE_MULTIPART
408
+
370
409
  json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
371
410
 
372
411
  # Add the input to each of these and do None-safety checks
@@ -402,11 +441,7 @@ class AsyncHttpClient:
402
441
  json=json_body,
403
442
  data=data_body,
404
443
  content=content,
405
- files=(
406
- convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
407
- if files is not None
408
- else None
409
- ),
444
+ files=request_files,
410
445
  timeout=timeout,
411
446
  )
412
447
 
@@ -440,11 +475,17 @@ class AsyncHttpClient:
440
475
  json: typing.Optional[typing.Any] = None,
441
476
  data: typing.Optional[typing.Any] = None,
442
477
  content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None,
443
- files: typing.Optional[typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]] = None,
478
+ files: typing.Optional[
479
+ typing.Union[
480
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]],
481
+ typing.List[typing.Tuple[str, File]],
482
+ ]
483
+ ] = None,
444
484
  headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
445
485
  request_options: typing.Optional[RequestOptions] = None,
446
486
  retries: int = 2,
447
487
  omit: typing.Optional[typing.Any] = None,
488
+ force_multipart: typing.Optional[bool] = None,
448
489
  ) -> typing.AsyncIterator[httpx.Response]:
449
490
  base_url = self.get_base_url(base_url)
450
491
  timeout = (
@@ -453,6 +494,15 @@ class AsyncHttpClient:
453
494
  else self.base_timeout()
454
495
  )
455
496
 
497
+ request_files: typing.Optional[RequestFiles] = (
498
+ convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
499
+ if (files is not None and files is not omit and isinstance(files, dict))
500
+ else None
501
+ )
502
+
503
+ if (request_files is None or len(request_files) == 0) and force_multipart:
504
+ request_files = FORCE_MULTIPART
505
+
456
506
  json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
457
507
 
458
508
  async with self.httpx_client.stream(
@@ -487,11 +537,7 @@ class AsyncHttpClient:
487
537
  json=json_body,
488
538
  data=data_body,
489
539
  content=content,
490
- files=(
491
- convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
492
- if files is not None
493
- else None
494
- ),
540
+ files=request_files,
495
541
  timeout=timeout,
496
542
  ) as stream:
497
543
  yield stream
@@ -5,20 +5,31 @@ from typing import Dict, Generic, TypeVar
5
5
  import httpx
6
6
 
7
7
  T = TypeVar("T")
8
+ """Generic to represent the underlying type of the data wrapped by the HTTP response."""
8
9
 
9
10
 
10
- class HttpResponse(Generic[T]):
11
+ class BaseHttpResponse:
12
+ """Minimalist HTTP response wrapper that exposes response headers."""
13
+
11
14
  _response: httpx.Response
12
- _data: T
13
15
 
14
- def __init__(self, response: httpx.Response, data: T):
16
+ def __init__(self, response: httpx.Response):
15
17
  self._response = response
16
- self._data = data
17
18
 
18
19
  @property
19
20
  def headers(self) -> Dict[str, str]:
20
21
  return dict(self._response.headers)
21
22
 
23
+
24
+ class HttpResponse(Generic[T], BaseHttpResponse):
25
+ """HTTP response wrapper that exposes response headers and data."""
26
+
27
+ _data: T
28
+
29
+ def __init__(self, response: httpx.Response, data: T):
30
+ super().__init__(response)
31
+ self._data = data
32
+
22
33
  @property
23
34
  def data(self) -> T:
24
35
  return self._data
@@ -27,18 +38,15 @@ class HttpResponse(Generic[T]):
27
38
  self._response.close()
28
39
 
29
40
 
30
- class AsyncHttpResponse(Generic[T]):
31
- _response: httpx.Response
41
+ class AsyncHttpResponse(Generic[T], BaseHttpResponse):
42
+ """HTTP response wrapper that exposes response headers and data."""
43
+
32
44
  _data: T
33
45
 
34
46
  def __init__(self, response: httpx.Response, data: T):
35
- self._response = response
47
+ super().__init__(response)
36
48
  self._data = data
37
49
 
38
- @property
39
- def headers(self) -> Dict[str, str]:
40
- return dict(self._response.headers)
41
-
42
50
  @property
43
51
  def data(self) -> T:
44
52
  return self._data
@@ -2,87 +2,65 @@
2
2
 
3
3
  # nopycln: file
4
4
  import datetime as dt
5
- import typing
6
5
  from collections import defaultdict
6
+ from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast
7
7
 
8
8
  import pydantic
9
- import typing_extensions
10
- from .datetime_utils import serialize_datetime
11
- from .serialization import convert_and_respect_annotation_metadata
12
9
 
13
10
  IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.")
14
11
 
15
12
  if IS_PYDANTIC_V2:
16
- # isort will try to reformat the comments on these imports, which breaks mypy
17
- # isort: off
18
- from pydantic.v1.datetime_parse import ( # type: ignore # pyright: ignore[reportMissingImports] # Pydantic v2
19
- parse_date as parse_date,
20
- )
21
- from pydantic.v1.datetime_parse import ( # pyright: ignore[reportMissingImports] # Pydantic v2
22
- parse_datetime as parse_datetime,
23
- )
24
- from pydantic.v1.json import ( # type: ignore # pyright: ignore[reportMissingImports] # Pydantic v2
25
- ENCODERS_BY_TYPE as encoders_by_type,
26
- )
27
- from pydantic.v1.typing import ( # type: ignore # pyright: ignore[reportMissingImports] # Pydantic v2
28
- get_args as get_args,
29
- )
30
- from pydantic.v1.typing import ( # pyright: ignore[reportMissingImports] # Pydantic v2
31
- get_origin as get_origin,
32
- )
33
- from pydantic.v1.typing import ( # pyright: ignore[reportMissingImports] # Pydantic v2
34
- is_literal_type as is_literal_type,
35
- )
36
- from pydantic.v1.typing import ( # pyright: ignore[reportMissingImports] # Pydantic v2
37
- is_union as is_union,
38
- )
39
- from pydantic.v1.fields import ModelField as ModelField # type: ignore # pyright: ignore[reportMissingImports] # Pydantic v2
13
+ from pydantic.v1.datetime_parse import parse_date as parse_date
14
+ from pydantic.v1.datetime_parse import parse_datetime as parse_datetime
15
+ from pydantic.v1.fields import ModelField as ModelField
16
+ from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined]
17
+ from pydantic.v1.typing import get_args as get_args
18
+ from pydantic.v1.typing import get_origin as get_origin
19
+ from pydantic.v1.typing import is_literal_type as is_literal_type
20
+ from pydantic.v1.typing import is_union as is_union
40
21
  else:
41
- from pydantic.datetime_parse import parse_date as parse_date # type: ignore # Pydantic v1
42
- from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore # Pydantic v1
43
- from pydantic.fields import ModelField as ModelField # type: ignore # Pydantic v1
44
- from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore # Pydantic v1
45
- from pydantic.typing import get_args as get_args # type: ignore # Pydantic v1
46
- from pydantic.typing import get_origin as get_origin # type: ignore # Pydantic v1
47
- from pydantic.typing import is_literal_type as is_literal_type # type: ignore # Pydantic v1
48
- from pydantic.typing import is_union as is_union # type: ignore # Pydantic v1
49
-
50
- # isort: on
22
+ from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef]
23
+ from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef]
24
+ from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef]
25
+ from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef]
26
+ from pydantic.typing import get_args as get_args # type: ignore[no-redef]
27
+ from pydantic.typing import get_origin as get_origin # type: ignore[no-redef]
28
+ from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef]
29
+ from pydantic.typing import is_union as is_union # type: ignore[no-redef]
51
30
 
31
+ from .datetime_utils import serialize_datetime
32
+ from .serialization import convert_and_respect_annotation_metadata
33
+ from typing_extensions import TypeAlias
52
34
 
53
- T = typing.TypeVar("T")
54
- Model = typing.TypeVar("Model", bound=pydantic.BaseModel)
35
+ T = TypeVar("T")
36
+ Model = TypeVar("Model", bound=pydantic.BaseModel)
55
37
 
56
38
 
57
- def parse_obj_as(type_: typing.Type[T], object_: typing.Any) -> T:
39
+ def parse_obj_as(type_: Type[T], object_: Any) -> T:
58
40
  dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read")
59
41
  if IS_PYDANTIC_V2:
60
- adapter = pydantic.TypeAdapter(type_) # type: ignore # Pydantic v2
42
+ adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined]
61
43
  return adapter.validate_python(dealiased_object)
62
- else:
63
- return pydantic.parse_obj_as(type_, dealiased_object)
44
+ return pydantic.parse_obj_as(type_, dealiased_object)
64
45
 
65
46
 
66
- def to_jsonable_with_fallback(
67
- obj: typing.Any, fallback_serializer: typing.Callable[[typing.Any], typing.Any]
68
- ) -> typing.Any:
47
+ def to_jsonable_with_fallback(obj: Any, fallback_serializer: Callable[[Any], Any]) -> Any:
69
48
  if IS_PYDANTIC_V2:
70
49
  from pydantic_core import to_jsonable_python
71
50
 
72
51
  return to_jsonable_python(obj, fallback=fallback_serializer)
73
- else:
74
- return fallback_serializer(obj)
52
+ return fallback_serializer(obj)
75
53
 
76
54
 
77
55
  class UniversalBaseModel(pydantic.BaseModel):
78
56
  if IS_PYDANTIC_V2:
79
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
57
+ model_config: ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( # type: ignore[typeddict-unknown-key]
80
58
  # Allow fields beginning with `model_` to be used in the model
81
59
  protected_namespaces=(),
82
- ) # type: ignore # Pydantic v2
60
+ )
83
61
 
84
- @pydantic.model_serializer(mode="wrap", when_used="json") # type: ignore # Pydantic v2
85
- def serialize_model(self, handler: pydantic.SerializerFunctionWrapHandler) -> typing.Any: # type: ignore # Pydantic v2
62
+ @pydantic.model_serializer(mode="wrap", when_used="json") # type: ignore[attr-defined]
63
+ def serialize_model(self, handler: pydantic.SerializerFunctionWrapHandler) -> Any: # type: ignore[name-defined]
86
64
  serialized = handler(self)
87
65
  data = {k: serialize_datetime(v) if isinstance(v, dt.datetime) else v for k, v in serialized.items()}
88
66
  return data
@@ -94,34 +72,28 @@ class UniversalBaseModel(pydantic.BaseModel):
94
72
  json_encoders = {dt.datetime: serialize_datetime}
95
73
 
96
74
  @classmethod
97
- def model_construct(
98
- cls: typing.Type["Model"], _fields_set: typing.Optional[typing.Set[str]] = None, **values: typing.Any
99
- ) -> "Model":
75
+ def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model":
100
76
  dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read")
101
77
  return cls.construct(_fields_set, **dealiased_object)
102
78
 
103
79
  @classmethod
104
- def construct(
105
- cls: typing.Type["Model"], _fields_set: typing.Optional[typing.Set[str]] = None, **values: typing.Any
106
- ) -> "Model":
80
+ def construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model":
107
81
  dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read")
108
82
  if IS_PYDANTIC_V2:
109
- return super().model_construct(_fields_set, **dealiased_object) # type: ignore # Pydantic v2
110
- else:
111
- return super().construct(_fields_set, **dealiased_object)
83
+ return super().model_construct(_fields_set, **dealiased_object) # type: ignore[misc]
84
+ return super().construct(_fields_set, **dealiased_object)
112
85
 
113
- def json(self, **kwargs: typing.Any) -> str:
114
- kwargs_with_defaults: typing.Any = {
86
+ def json(self, **kwargs: Any) -> str:
87
+ kwargs_with_defaults = {
115
88
  "by_alias": True,
116
89
  "exclude_unset": True,
117
90
  **kwargs,
118
91
  }
119
92
  if IS_PYDANTIC_V2:
120
- return super().model_dump_json(**kwargs_with_defaults) # type: ignore # Pydantic v2
121
- else:
122
- return super().json(**kwargs_with_defaults)
93
+ return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc]
94
+ return super().json(**kwargs_with_defaults)
123
95
 
124
- def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
96
+ def dict(self, **kwargs: Any) -> Dict[str, Any]:
125
97
  """
126
98
  Override the default dict method to `exclude_unset` by default. This function patches
127
99
  `exclude_unset` to work include fields within non-None default values.
@@ -132,21 +104,21 @@ class UniversalBaseModel(pydantic.BaseModel):
132
104
  # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models
133
105
  # that we have less control over, and this is less intrusive than custom serializers for now.
134
106
  if IS_PYDANTIC_V2:
135
- kwargs_with_defaults_exclude_unset: typing.Any = {
107
+ kwargs_with_defaults_exclude_unset = {
136
108
  **kwargs,
137
109
  "by_alias": True,
138
110
  "exclude_unset": True,
139
111
  "exclude_none": False,
140
112
  }
141
- kwargs_with_defaults_exclude_none: typing.Any = {
113
+ kwargs_with_defaults_exclude_none = {
142
114
  **kwargs,
143
115
  "by_alias": True,
144
116
  "exclude_none": True,
145
117
  "exclude_unset": False,
146
118
  }
147
119
  dict_dump = deep_union_pydantic_dicts(
148
- super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore # Pydantic v2
149
- super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore # Pydantic v2
120
+ super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc]
121
+ super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc]
150
122
  )
151
123
 
152
124
  else:
@@ -166,7 +138,7 @@ class UniversalBaseModel(pydantic.BaseModel):
166
138
  if default is not None:
167
139
  self.__fields_set__.add(name)
168
140
 
169
- kwargs_with_defaults_exclude_unset_include_fields: typing.Any = {
141
+ kwargs_with_defaults_exclude_unset_include_fields = {
170
142
  "by_alias": True,
171
143
  "exclude_unset": True,
172
144
  "include": _fields_set,
@@ -178,12 +150,10 @@ class UniversalBaseModel(pydantic.BaseModel):
178
150
  return convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write")
179
151
 
180
152
 
181
- def _union_list_of_pydantic_dicts(
182
- source: typing.List[typing.Any], destination: typing.List[typing.Any]
183
- ) -> typing.List[typing.Any]:
184
- converted_list: typing.List[typing.Any] = []
153
+ def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]:
154
+ converted_list: List[Any] = []
185
155
  for i, item in enumerate(source):
186
- destination_value = destination[i] # type: ignore
156
+ destination_value = destination[i]
187
157
  if isinstance(item, dict):
188
158
  converted_list.append(deep_union_pydantic_dicts(item, destination_value))
189
159
  elif isinstance(item, list):
@@ -193,9 +163,7 @@ def _union_list_of_pydantic_dicts(
193
163
  return converted_list
194
164
 
195
165
 
196
- def deep_union_pydantic_dicts(
197
- source: typing.Dict[str, typing.Any], destination: typing.Dict[str, typing.Any]
198
- ) -> typing.Dict[str, typing.Any]:
166
+ def deep_union_pydantic_dicts(source: Dict[str, Any], destination: Dict[str, Any]) -> Dict[str, Any]:
199
167
  for key, value in source.items():
200
168
  node = destination.setdefault(key, {})
201
169
  if isinstance(value, dict):
@@ -213,18 +181,16 @@ def deep_union_pydantic_dicts(
213
181
 
214
182
  if IS_PYDANTIC_V2:
215
183
 
216
- class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore # Pydantic v2
184
+ class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[misc, name-defined, type-arg]
217
185
  pass
218
186
 
219
- UniversalRootModel: typing_extensions.TypeAlias = V2RootModel # type: ignore
187
+ UniversalRootModel: TypeAlias = V2RootModel # type: ignore[misc]
220
188
  else:
221
- UniversalRootModel: typing_extensions.TypeAlias = UniversalBaseModel # type: ignore
189
+ UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef]
222
190
 
223
191
 
224
- def encode_by_type(o: typing.Any) -> typing.Any:
225
- encoders_by_class_tuples: typing.Dict[typing.Callable[[typing.Any], typing.Any], typing.Tuple[typing.Any, ...]] = (
226
- defaultdict(tuple)
227
- )
192
+ def encode_by_type(o: Any) -> Any:
193
+ encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple)
228
194
  for type_, encoder in encoders_by_type.items():
229
195
  encoders_by_class_tuples[encoder] += (type_,)
230
196
 
@@ -235,54 +201,49 @@ def encode_by_type(o: typing.Any) -> typing.Any:
235
201
  return encoder(o)
236
202
 
237
203
 
238
- def update_forward_refs(model: typing.Type["Model"], **localns: typing.Any) -> None:
204
+ def update_forward_refs(model: Type["Model"], **localns: Any) -> None:
239
205
  if IS_PYDANTIC_V2:
240
- model.model_rebuild(raise_errors=False) # type: ignore # Pydantic v2
206
+ model.model_rebuild(raise_errors=False) # type: ignore[attr-defined]
241
207
  else:
242
208
  model.update_forward_refs(**localns)
243
209
 
244
210
 
245
211
  # Mirrors Pydantic's internal typing
246
- AnyCallable = typing.Callable[..., typing.Any]
212
+ AnyCallable = Callable[..., Any]
247
213
 
248
214
 
249
215
  def universal_root_validator(
250
216
  pre: bool = False,
251
- ) -> typing.Callable[[AnyCallable], AnyCallable]:
217
+ ) -> Callable[[AnyCallable], AnyCallable]:
252
218
  def decorator(func: AnyCallable) -> AnyCallable:
253
219
  if IS_PYDANTIC_V2:
254
- return pydantic.model_validator(mode="before" if pre else "after")(func) # type: ignore # Pydantic v2
255
- else:
256
- return pydantic.root_validator(pre=pre)(func) # type: ignore # Pydantic v1
220
+ return cast(AnyCallable, pydantic.model_validator(mode="before" if pre else "after")(func)) # type: ignore[attr-defined]
221
+ return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload]
257
222
 
258
223
  return decorator
259
224
 
260
225
 
261
- def universal_field_validator(field_name: str, pre: bool = False) -> typing.Callable[[AnyCallable], AnyCallable]:
226
+ def universal_field_validator(field_name: str, pre: bool = False) -> Callable[[AnyCallable], AnyCallable]:
262
227
  def decorator(func: AnyCallable) -> AnyCallable:
263
228
  if IS_PYDANTIC_V2:
264
- return pydantic.field_validator(field_name, mode="before" if pre else "after")(func) # type: ignore # Pydantic v2
265
- else:
266
- return pydantic.validator(field_name, pre=pre)(func) # type: ignore # Pydantic v1
229
+ return cast(AnyCallable, pydantic.field_validator(field_name, mode="before" if pre else "after")(func)) # type: ignore[attr-defined]
230
+ return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func))
267
231
 
268
232
  return decorator
269
233
 
270
234
 
271
- PydanticField = typing.Union[ModelField, pydantic.fields.FieldInfo]
235
+ PydanticField = Union[ModelField, pydantic.fields.FieldInfo]
272
236
 
273
237
 
274
- def _get_model_fields(
275
- model: typing.Type["Model"],
276
- ) -> typing.Mapping[str, PydanticField]:
238
+ def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]:
277
239
  if IS_PYDANTIC_V2:
278
- return model.model_fields # type: ignore # Pydantic v2
279
- else:
280
- return model.__fields__ # type: ignore # Pydantic v1
240
+ return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined]
241
+ return cast(Mapping[str, PydanticField], model.__fields__)
281
242
 
282
243
 
283
- def _get_field_default(field: PydanticField) -> typing.Any:
244
+ def _get_field_default(field: PydanticField) -> Any:
284
245
  try:
285
- value = field.get_default() # type: ignore # Pydantic < v1.10.15
246
+ value = field.get_default() # type: ignore[union-attr]
286
247
  except:
287
248
  value = field.default
288
249
  if IS_PYDANTIC_V2:
sarvamai/environment.py CHANGED
@@ -1,7 +1,14 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- import enum
3
+ from __future__ import annotations
4
4
 
5
5
 
6
- class SarvamAIEnvironment(enum.Enum):
7
- PRODUCTION = "https://api.sarvam.ai"
6
+ class SarvamAIEnvironment:
7
+ PRODUCTION: SarvamAIEnvironment
8
+
9
+ def __init__(self, *, base: str, production: str):
10
+ self.base = base
11
+ self.production = production
12
+
13
+
14
+ SarvamAIEnvironment.PRODUCTION = SarvamAIEnvironment(base="https://api.sarvam.ai", production="wss://api.sarvam.ai")
@@ -1,5 +1,7 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
+ # isort: skip_file
4
+
3
5
  from .bad_request_error import BadRequestError
4
6
  from .forbidden_error import ForbiddenError
5
7
  from .internal_server_error import InternalServerError
@@ -1,9 +1,10 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- from ..core.api_error import ApiError
4
3
  import typing
5
4
 
5
+ from ..core.api_error import ApiError
6
+
6
7
 
7
8
  class BadRequestError(ApiError):
8
- def __init__(self, body: typing.Optional[typing.Any]):
9
- super().__init__(status_code=400, body=body)
9
+ def __init__(self, body: typing.Optional[typing.Any], headers: typing.Optional[typing.Dict[str, str]] = None):
10
+ super().__init__(status_code=400, headers=headers, body=body)
@@ -1,9 +1,10 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- from ..core.api_error import ApiError
4
3
  import typing
5
4
 
5
+ from ..core.api_error import ApiError
6
+
6
7
 
7
8
  class ForbiddenError(ApiError):
8
- def __init__(self, body: typing.Optional[typing.Any]):
9
- super().__init__(status_code=403, body=body)
9
+ def __init__(self, body: typing.Optional[typing.Any], headers: typing.Optional[typing.Dict[str, str]] = None):
10
+ super().__init__(status_code=403, headers=headers, body=body)
@@ -1,9 +1,10 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- from ..core.api_error import ApiError
4
3
  import typing
5
4
 
5
+ from ..core.api_error import ApiError
6
+
6
7
 
7
8
  class InternalServerError(ApiError):
8
- def __init__(self, body: typing.Optional[typing.Any]):
9
- super().__init__(status_code=500, body=body)
9
+ def __init__(self, body: typing.Optional[typing.Any], headers: typing.Optional[typing.Dict[str, str]] = None):
10
+ super().__init__(status_code=500, headers=headers, body=body)