sarvamai 0.1.5a1__py3-none-any.whl → 0.1.5a3__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 (65) hide show
  1. sarvamai/__init__.py +2 -0
  2. sarvamai/chat/__init__.py +2 -0
  3. sarvamai/chat/client.py +9 -22
  4. sarvamai/chat/raw_client.py +46 -47
  5. sarvamai/client.py +8 -12
  6. sarvamai/core/__init__.py +2 -0
  7. sarvamai/core/api_error.py +13 -5
  8. sarvamai/core/client_wrapper.py +5 -5
  9. sarvamai/core/force_multipart.py +16 -0
  10. sarvamai/core/http_client.py +70 -24
  11. sarvamai/core/http_response.py +19 -11
  12. sarvamai/core/pydantic_utilities.py +69 -108
  13. sarvamai/errors/__init__.py +2 -0
  14. sarvamai/errors/bad_request_error.py +4 -3
  15. sarvamai/errors/forbidden_error.py +4 -3
  16. sarvamai/errors/internal_server_error.py +4 -3
  17. sarvamai/errors/service_unavailable_error.py +4 -2
  18. sarvamai/errors/too_many_requests_error.py +4 -3
  19. sarvamai/errors/unprocessable_entity_error.py +4 -3
  20. sarvamai/requests/__init__.py +2 -0
  21. sarvamai/requests/chat_completion_request_message.py +3 -1
  22. sarvamai/requests/create_chat_completion_response.py +2 -2
  23. sarvamai/requests/diarized_transcript.py +2 -1
  24. sarvamai/requests/error_details.py +0 -1
  25. sarvamai/requests/language_identification_response.py +0 -1
  26. sarvamai/requests/speech_to_text_response.py +1 -2
  27. sarvamai/requests/speech_to_text_translate_response.py +0 -1
  28. sarvamai/requests/text_to_speech_response.py +2 -2
  29. sarvamai/requests/timestamps_model.py +2 -1
  30. sarvamai/requests/translation_response.py +0 -1
  31. sarvamai/requests/transliteration_response.py +0 -1
  32. sarvamai/speech_to_text/__init__.py +2 -0
  33. sarvamai/speech_to_text/client.py +13 -14
  34. sarvamai/speech_to_text/raw_client.py +95 -68
  35. sarvamai/text/__init__.py +2 -0
  36. sarvamai/text/client.py +23 -24
  37. sarvamai/text/raw_client.py +120 -91
  38. sarvamai/text_to_speech/__init__.py +2 -0
  39. sarvamai/text_to_speech/client.py +10 -11
  40. sarvamai/text_to_speech/raw_client.py +47 -38
  41. sarvamai/types/__init__.py +2 -0
  42. sarvamai/types/chat_completion_request_assistant_message.py +3 -3
  43. sarvamai/types/chat_completion_request_message.py +3 -2
  44. sarvamai/types/chat_completion_request_system_message.py +3 -3
  45. sarvamai/types/chat_completion_request_user_message.py +3 -3
  46. sarvamai/types/chat_completion_response_message.py +3 -3
  47. sarvamai/types/choice.py +4 -4
  48. sarvamai/types/completion_usage.py +3 -3
  49. sarvamai/types/create_chat_completion_response.py +3 -3
  50. sarvamai/types/diarized_entry.py +3 -3
  51. sarvamai/types/diarized_transcript.py +3 -3
  52. sarvamai/types/error_details.py +3 -3
  53. sarvamai/types/error_message.py +4 -4
  54. sarvamai/types/language_identification_response.py +2 -2
  55. sarvamai/types/speech_to_text_response.py +3 -3
  56. sarvamai/types/speech_to_text_translate_response.py +3 -3
  57. sarvamai/types/text_to_speech_response.py +2 -2
  58. sarvamai/types/timestamps_model.py +2 -2
  59. sarvamai/types/translation_response.py +2 -2
  60. sarvamai/types/transliteration_response.py +2 -2
  61. sarvamai-0.1.5a3.dist-info/METADATA +27 -0
  62. sarvamai-0.1.5a3.dist-info/RECORD +108 -0
  63. sarvamai-0.1.5a1.dist-info/METADATA +0 -174
  64. sarvamai-0.1.5a1.dist-info/RECORD +0 -107
  65. {sarvamai-0.1.5a1.dist-info → sarvamai-0.1.5a3.dist-info}/WHEEL +0 -0
sarvamai/__init__.py CHANGED
@@ -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 .types import (
4
6
  ChatCompletionRequestAssistantMessage,
5
7
  ChatCompletionRequestMessage,
sarvamai/chat/__init__.py CHANGED
@@ -1,2 +1,4 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
+ # isort: skip_file
4
+
sarvamai/chat/client.py CHANGED
@@ -1,15 +1,14 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
3
  import typing
4
- from ..core.client_wrapper import SyncClientWrapper
5
- from .raw_client import RawChatClient
4
+
5
+ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
6
+ from ..core.request_options import RequestOptions
6
7
  from ..requests.chat_completion_request_message import ChatCompletionRequestMessageParams
7
- from ..types.reasoning_effort import ReasoningEffort
8
8
  from ..requests.stop_configuration import StopConfigurationParams
9
- from ..core.request_options import RequestOptions
10
9
  from ..types.create_chat_completion_response import CreateChatCompletionResponse
11
- from ..core.client_wrapper import AsyncClientWrapper
12
- from .raw_client import AsyncRawChatClient
10
+ from ..types.reasoning_effort import ReasoningEffort
11
+ from .raw_client import AsyncRawChatClient, RawChatClient
13
12
 
14
13
  # this is used as the default value for optional parameters
15
14
  OMIT = typing.cast(typing.Any, ...)
@@ -33,7 +32,6 @@ class ChatClient:
33
32
  def completions(
34
33
  self,
35
34
  *,
36
- authorization: str,
37
35
  messages: typing.Sequence[ChatCompletionRequestMessageParams],
38
36
  temperature: typing.Optional[float] = OMIT,
39
37
  top_p: typing.Optional[float] = OMIT,
@@ -53,9 +51,6 @@ class ChatClient:
53
51
 
54
52
  Parameters
55
53
  ----------
56
- authorization : str
57
- Bearer token for authentication. Format: "Bearer <token>"
58
-
59
54
  messages : typing.Sequence[ChatCompletionRequestMessageParams]
60
55
  A list of messages comprising the conversation so far.
61
56
 
@@ -119,12 +114,10 @@ class ChatClient:
119
114
  api_subscription_key="YOUR_API_SUBSCRIPTION_KEY",
120
115
  )
121
116
  client.chat.completions(
122
- authorization="Authorization",
123
117
  messages=[{"content": "content", "role": "assistant"}],
124
118
  )
125
119
  """
126
- response = self._raw_client.completions(
127
- authorization=authorization,
120
+ _response = self._raw_client.completions(
128
121
  messages=messages,
129
122
  temperature=temperature,
130
123
  top_p=top_p,
@@ -139,7 +132,7 @@ class ChatClient:
139
132
  wiki_grounding=wiki_grounding,
140
133
  request_options=request_options,
141
134
  )
142
- return response.data
135
+ return _response.data
143
136
 
144
137
 
145
138
  class AsyncChatClient:
@@ -160,7 +153,6 @@ class AsyncChatClient:
160
153
  async def completions(
161
154
  self,
162
155
  *,
163
- authorization: str,
164
156
  messages: typing.Sequence[ChatCompletionRequestMessageParams],
165
157
  temperature: typing.Optional[float] = OMIT,
166
158
  top_p: typing.Optional[float] = OMIT,
@@ -180,9 +172,6 @@ class AsyncChatClient:
180
172
 
181
173
  Parameters
182
174
  ----------
183
- authorization : str
184
- Bearer token for authentication. Format: "Bearer <token>"
185
-
186
175
  messages : typing.Sequence[ChatCompletionRequestMessageParams]
187
176
  A list of messages comprising the conversation so far.
188
177
 
@@ -251,15 +240,13 @@ class AsyncChatClient:
251
240
 
252
241
  async def main() -> None:
253
242
  await client.chat.completions(
254
- authorization="Authorization",
255
243
  messages=[{"content": "content", "role": "assistant"}],
256
244
  )
257
245
 
258
246
 
259
247
  asyncio.run(main())
260
248
  """
261
- response = await self._raw_client.completions(
262
- authorization=authorization,
249
+ _response = await self._raw_client.completions(
263
250
  messages=messages,
264
251
  temperature=temperature,
265
252
  top_p=top_p,
@@ -274,4 +261,4 @@ class AsyncChatClient:
274
261
  wiki_grounding=wiki_grounding,
275
262
  request_options=request_options,
276
263
  )
277
- return response.data
264
+ return _response.data
@@ -1,24 +1,23 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
3
  import typing
4
- from ..core.client_wrapper import SyncClientWrapper
5
- from ..requests.chat_completion_request_message import ChatCompletionRequestMessageParams
6
- from ..types.reasoning_effort import ReasoningEffort
7
- from ..requests.stop_configuration import StopConfigurationParams
4
+ from json.decoder import JSONDecodeError
5
+
6
+ from ..core.api_error import ApiError
7
+ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
8
+ from ..core.http_response import AsyncHttpResponse, HttpResponse
9
+ from ..core.pydantic_utilities import parse_obj_as
8
10
  from ..core.request_options import RequestOptions
9
- from ..core.http_response import HttpResponse
10
- from ..types.create_chat_completion_response import CreateChatCompletionResponse
11
11
  from ..core.serialization import convert_and_respect_annotation_metadata
12
- from ..core.pydantic_utilities import parse_obj_as
13
12
  from ..errors.bad_request_error import BadRequestError
14
13
  from ..errors.forbidden_error import ForbiddenError
15
- from ..errors.unprocessable_entity_error import UnprocessableEntityError
16
- from ..errors.too_many_requests_error import TooManyRequestsError
17
14
  from ..errors.internal_server_error import InternalServerError
18
- from json.decoder import JSONDecodeError
19
- from ..core.api_error import ApiError
20
- from ..core.client_wrapper import AsyncClientWrapper
21
- from ..core.http_response import AsyncHttpResponse
15
+ from ..errors.too_many_requests_error import TooManyRequestsError
16
+ from ..errors.unprocessable_entity_error import UnprocessableEntityError
17
+ from ..requests.chat_completion_request_message import ChatCompletionRequestMessageParams
18
+ from ..requests.stop_configuration import StopConfigurationParams
19
+ from ..types.create_chat_completion_response import CreateChatCompletionResponse
20
+ from ..types.reasoning_effort import ReasoningEffort
22
21
 
23
22
  # this is used as the default value for optional parameters
24
23
  OMIT = typing.cast(typing.Any, ...)
@@ -31,7 +30,6 @@ class RawChatClient:
31
30
  def completions(
32
31
  self,
33
32
  *,
34
- authorization: str,
35
33
  messages: typing.Sequence[ChatCompletionRequestMessageParams],
36
34
  temperature: typing.Optional[float] = OMIT,
37
35
  top_p: typing.Optional[float] = OMIT,
@@ -51,9 +49,6 @@ class RawChatClient:
51
49
 
52
50
  Parameters
53
51
  ----------
54
- authorization : str
55
- Bearer token for authentication. Format: "Bearer <token>"
56
-
57
52
  messages : typing.Sequence[ChatCompletionRequestMessageParams]
58
53
  A list of messages comprising the conversation so far.
59
54
 
@@ -134,7 +129,6 @@ class RawChatClient:
134
129
  },
135
130
  headers={
136
131
  "content-type": "application/json",
137
- "Authorization": str(authorization) if authorization is not None else None,
138
132
  },
139
133
  request_options=request_options,
140
134
  omit=OMIT,
@@ -151,58 +145,63 @@ class RawChatClient:
151
145
  return HttpResponse(response=_response, data=_data)
152
146
  if _response.status_code == 400:
153
147
  raise BadRequestError(
154
- typing.cast(
148
+ headers=dict(_response.headers),
149
+ body=typing.cast(
155
150
  typing.Optional[typing.Any],
156
151
  parse_obj_as(
157
152
  type_=typing.Optional[typing.Any], # type: ignore
158
153
  object_=_response.json(),
159
154
  ),
160
- )
155
+ ),
161
156
  )
162
157
  if _response.status_code == 403:
163
158
  raise ForbiddenError(
164
- typing.cast(
159
+ headers=dict(_response.headers),
160
+ body=typing.cast(
165
161
  typing.Optional[typing.Any],
166
162
  parse_obj_as(
167
163
  type_=typing.Optional[typing.Any], # type: ignore
168
164
  object_=_response.json(),
169
165
  ),
170
- )
166
+ ),
171
167
  )
172
168
  if _response.status_code == 422:
173
169
  raise UnprocessableEntityError(
174
- typing.cast(
170
+ headers=dict(_response.headers),
171
+ body=typing.cast(
175
172
  typing.Optional[typing.Any],
176
173
  parse_obj_as(
177
174
  type_=typing.Optional[typing.Any], # type: ignore
178
175
  object_=_response.json(),
179
176
  ),
180
- )
177
+ ),
181
178
  )
182
179
  if _response.status_code == 429:
183
180
  raise TooManyRequestsError(
184
- typing.cast(
181
+ headers=dict(_response.headers),
182
+ body=typing.cast(
185
183
  typing.Optional[typing.Any],
186
184
  parse_obj_as(
187
185
  type_=typing.Optional[typing.Any], # type: ignore
188
186
  object_=_response.json(),
189
187
  ),
190
- )
188
+ ),
191
189
  )
192
190
  if _response.status_code == 500:
193
191
  raise InternalServerError(
194
- typing.cast(
192
+ headers=dict(_response.headers),
193
+ body=typing.cast(
195
194
  typing.Optional[typing.Any],
196
195
  parse_obj_as(
197
196
  type_=typing.Optional[typing.Any], # type: ignore
198
197
  object_=_response.json(),
199
198
  ),
200
- )
199
+ ),
201
200
  )
202
201
  _response_json = _response.json()
203
202
  except JSONDecodeError:
204
- raise ApiError(status_code=_response.status_code, body=_response.text)
205
- raise ApiError(status_code=_response.status_code, body=_response_json)
203
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
204
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
206
205
 
207
206
 
208
207
  class AsyncRawChatClient:
@@ -212,7 +211,6 @@ class AsyncRawChatClient:
212
211
  async def completions(
213
212
  self,
214
213
  *,
215
- authorization: str,
216
214
  messages: typing.Sequence[ChatCompletionRequestMessageParams],
217
215
  temperature: typing.Optional[float] = OMIT,
218
216
  top_p: typing.Optional[float] = OMIT,
@@ -232,9 +230,6 @@ class AsyncRawChatClient:
232
230
 
233
231
  Parameters
234
232
  ----------
235
- authorization : str
236
- Bearer token for authentication. Format: "Bearer <token>"
237
-
238
233
  messages : typing.Sequence[ChatCompletionRequestMessageParams]
239
234
  A list of messages comprising the conversation so far.
240
235
 
@@ -315,7 +310,6 @@ class AsyncRawChatClient:
315
310
  },
316
311
  headers={
317
312
  "content-type": "application/json",
318
- "Authorization": str(authorization) if authorization is not None else None,
319
313
  },
320
314
  request_options=request_options,
321
315
  omit=OMIT,
@@ -332,55 +326,60 @@ class AsyncRawChatClient:
332
326
  return AsyncHttpResponse(response=_response, data=_data)
333
327
  if _response.status_code == 400:
334
328
  raise BadRequestError(
335
- typing.cast(
329
+ headers=dict(_response.headers),
330
+ body=typing.cast(
336
331
  typing.Optional[typing.Any],
337
332
  parse_obj_as(
338
333
  type_=typing.Optional[typing.Any], # type: ignore
339
334
  object_=_response.json(),
340
335
  ),
341
- )
336
+ ),
342
337
  )
343
338
  if _response.status_code == 403:
344
339
  raise ForbiddenError(
345
- typing.cast(
340
+ headers=dict(_response.headers),
341
+ body=typing.cast(
346
342
  typing.Optional[typing.Any],
347
343
  parse_obj_as(
348
344
  type_=typing.Optional[typing.Any], # type: ignore
349
345
  object_=_response.json(),
350
346
  ),
351
- )
347
+ ),
352
348
  )
353
349
  if _response.status_code == 422:
354
350
  raise UnprocessableEntityError(
355
- typing.cast(
351
+ headers=dict(_response.headers),
352
+ body=typing.cast(
356
353
  typing.Optional[typing.Any],
357
354
  parse_obj_as(
358
355
  type_=typing.Optional[typing.Any], # type: ignore
359
356
  object_=_response.json(),
360
357
  ),
361
- )
358
+ ),
362
359
  )
363
360
  if _response.status_code == 429:
364
361
  raise TooManyRequestsError(
365
- typing.cast(
362
+ headers=dict(_response.headers),
363
+ body=typing.cast(
366
364
  typing.Optional[typing.Any],
367
365
  parse_obj_as(
368
366
  type_=typing.Optional[typing.Any], # type: ignore
369
367
  object_=_response.json(),
370
368
  ),
371
- )
369
+ ),
372
370
  )
373
371
  if _response.status_code == 500:
374
372
  raise InternalServerError(
375
- typing.cast(
373
+ headers=dict(_response.headers),
374
+ body=typing.cast(
376
375
  typing.Optional[typing.Any],
377
376
  parse_obj_as(
378
377
  type_=typing.Optional[typing.Any], # type: ignore
379
378
  object_=_response.json(),
380
379
  ),
381
- )
380
+ ),
382
381
  )
383
382
  _response_json = _response.json()
384
383
  except JSONDecodeError:
385
- raise ApiError(status_code=_response.status_code, body=_response.text)
386
- raise ApiError(status_code=_response.status_code, body=_response_json)
384
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
385
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
sarvamai/client.py CHANGED
@@ -1,20 +1,16 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- from .environment import SarvamAIEnvironment
4
- import typing
5
3
  import os
4
+ import typing
5
+
6
6
  import httpx
7
+ from .chat.client import AsyncChatClient, ChatClient
7
8
  from .core.api_error import ApiError
8
- from .core.client_wrapper import SyncClientWrapper
9
- from .text.client import TextClient
10
- from .speech_to_text.client import SpeechToTextClient
11
- from .text_to_speech.client import TextToSpeechClient
12
- from .chat.client import ChatClient
13
- from .core.client_wrapper import AsyncClientWrapper
14
- from .text.client import AsyncTextClient
15
- from .speech_to_text.client import AsyncSpeechToTextClient
16
- from .text_to_speech.client import AsyncTextToSpeechClient
17
- from .chat.client import AsyncChatClient
9
+ from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
10
+ from .environment import SarvamAIEnvironment
11
+ from .speech_to_text.client import AsyncSpeechToTextClient, SpeechToTextClient
12
+ from .text.client import AsyncTextClient, TextClient
13
+ from .text_to_speech.client import AsyncTextToSpeechClient, TextToSpeechClient
18
14
 
19
15
 
20
16
  class SarvamAI:
sarvamai/core/__init__.py CHANGED
@@ -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 .api_error import ApiError
4
6
  from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper
5
7
  from .datetime_utils import serialize_datetime
@@ -1,15 +1,23 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- import typing
3
+ from typing import Any, Dict, Optional
4
4
 
5
5
 
6
6
  class ApiError(Exception):
7
- status_code: typing.Optional[int]
8
- body: typing.Any
7
+ headers: Optional[Dict[str, str]]
8
+ status_code: Optional[int]
9
+ body: Any
9
10
 
10
- def __init__(self, *, status_code: typing.Optional[int] = None, body: typing.Any = None):
11
+ def __init__(
12
+ self,
13
+ *,
14
+ headers: Optional[Dict[str, str]] = None,
15
+ status_code: Optional[int] = None,
16
+ body: Any = None,
17
+ ) -> None:
18
+ self.headers = headers
11
19
  self.status_code = status_code
12
20
  self.body = body
13
21
 
14
22
  def __str__(self) -> str:
15
- return f"status_code: {self.status_code}, body: {self.body}"
23
+ return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}"
@@ -1,10 +1,10 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- from ..environment import SarvamAIEnvironment
4
3
  import typing
4
+
5
5
  import httpx
6
- from .http_client import HttpClient
7
- from .http_client import AsyncHttpClient
6
+ from ..environment import SarvamAIEnvironment
7
+ from .http_client import AsyncHttpClient, HttpClient
8
8
 
9
9
 
10
10
  class BaseClientWrapper:
@@ -17,10 +17,10 @@ class BaseClientWrapper:
17
17
 
18
18
  def get_headers(self) -> typing.Dict[str, str]:
19
19
  headers: typing.Dict[str, str] = {
20
- "User-Agent": "sarvamai/0.1.5a1",
20
+ "User-Agent": "sarvamai/0.1.5a3",
21
21
  "X-Fern-Language": "Python",
22
22
  "X-Fern-SDK-Name": "sarvamai",
23
- "X-Fern-SDK-Version": "0.1.5a1",
23
+ "X-Fern-SDK-Version": "0.1.5a3",
24
24
  }
25
25
  headers["api-subscription-key"] = self.api_subscription_key
26
26
  return headers
@@ -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