spitch 1.25.0__py3-none-any.whl → 1.27.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.

Potentially problematic release.


This version of spitch might be problematic. Click here for more details.

spitch/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
+ import typing as _t
4
+
3
5
  from . import types
4
6
  from ._types import NOT_GIVEN, NoneType, NotGiven, Transport, ProxiesTypes
5
7
  from ._utils import file_from_path
@@ -67,6 +69,9 @@ __all__ = [
67
69
  "DefaultAsyncHttpxClient",
68
70
  ]
69
71
 
72
+ if not _t.TYPE_CHECKING:
73
+ from ._utils._resources_proxy import resources as resources
74
+
70
75
  _setup_logging()
71
76
 
72
77
  # Update the __module__ attribute for exported symbols so that
spitch/_base_client.py CHANGED
@@ -98,7 +98,11 @@ _StreamT = TypeVar("_StreamT", bound=Stream[Any])
98
98
  _AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any])
99
99
 
100
100
  if TYPE_CHECKING:
101
- from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
101
+ from httpx._config import (
102
+ DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage]
103
+ )
104
+
105
+ HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG
102
106
  else:
103
107
  try:
104
108
  from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
@@ -115,6 +119,7 @@ class PageInfo:
115
119
 
116
120
  url: URL | NotGiven
117
121
  params: Query | NotGiven
122
+ json: Body | NotGiven
118
123
 
119
124
  @overload
120
125
  def __init__(
@@ -130,19 +135,30 @@ class PageInfo:
130
135
  params: Query,
131
136
  ) -> None: ...
132
137
 
138
+ @overload
139
+ def __init__(
140
+ self,
141
+ *,
142
+ json: Body,
143
+ ) -> None: ...
144
+
133
145
  def __init__(
134
146
  self,
135
147
  *,
136
148
  url: URL | NotGiven = NOT_GIVEN,
149
+ json: Body | NotGiven = NOT_GIVEN,
137
150
  params: Query | NotGiven = NOT_GIVEN,
138
151
  ) -> None:
139
152
  self.url = url
153
+ self.json = json
140
154
  self.params = params
141
155
 
142
156
  @override
143
157
  def __repr__(self) -> str:
144
158
  if self.url:
145
159
  return f"{self.__class__.__name__}(url={self.url})"
160
+ if self.json:
161
+ return f"{self.__class__.__name__}(json={self.json})"
146
162
  return f"{self.__class__.__name__}(params={self.params})"
147
163
 
148
164
 
@@ -191,6 +207,19 @@ class BasePage(GenericModel, Generic[_T]):
191
207
  options.url = str(url)
192
208
  return options
193
209
 
210
+ if not isinstance(info.json, NotGiven):
211
+ if not is_mapping(info.json):
212
+ raise TypeError("Pagination is only supported with mappings")
213
+
214
+ if not options.json_data:
215
+ options.json_data = {**info.json}
216
+ else:
217
+ if not is_mapping(options.json_data):
218
+ raise TypeError("Pagination is only supported with mappings")
219
+
220
+ options.json_data = {**options.json_data, **info.json}
221
+ return options
222
+
194
223
  raise ValueError("Unexpected PageInfo state")
195
224
 
196
225
 
@@ -408,8 +437,8 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
408
437
  headers = httpx.Headers(headers_dict)
409
438
 
410
439
  idempotency_header = self._idempotency_header
411
- if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
412
- headers[idempotency_header] = options.idempotency_key or self._idempotency_key()
440
+ if idempotency_header and options.idempotency_key and idempotency_header not in headers:
441
+ headers[idempotency_header] = options.idempotency_key
413
442
 
414
443
  # Don't set these headers if they were already set or removed by the caller. We check
415
444
  # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case.
@@ -873,7 +902,6 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
873
902
  self,
874
903
  cast_to: Type[ResponseT],
875
904
  options: FinalRequestOptions,
876
- remaining_retries: Optional[int] = None,
877
905
  *,
878
906
  stream: Literal[True],
879
907
  stream_cls: Type[_StreamT],
@@ -884,7 +912,6 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
884
912
  self,
885
913
  cast_to: Type[ResponseT],
886
914
  options: FinalRequestOptions,
887
- remaining_retries: Optional[int] = None,
888
915
  *,
889
916
  stream: Literal[False] = False,
890
917
  ) -> ResponseT: ...
@@ -894,7 +921,6 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
894
921
  self,
895
922
  cast_to: Type[ResponseT],
896
923
  options: FinalRequestOptions,
897
- remaining_retries: Optional[int] = None,
898
924
  *,
899
925
  stream: bool = False,
900
926
  stream_cls: Type[_StreamT] | None = None,
@@ -904,121 +930,112 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
904
930
  self,
905
931
  cast_to: Type[ResponseT],
906
932
  options: FinalRequestOptions,
907
- remaining_retries: Optional[int] = None,
908
933
  *,
909
934
  stream: bool = False,
910
935
  stream_cls: type[_StreamT] | None = None,
911
936
  ) -> ResponseT | _StreamT:
912
- if remaining_retries is not None:
913
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
914
- else:
915
- retries_taken = 0
916
-
917
- return self._request(
918
- cast_to=cast_to,
919
- options=options,
920
- stream=stream,
921
- stream_cls=stream_cls,
922
- retries_taken=retries_taken,
923
- )
937
+ cast_to = self._maybe_override_cast_to(cast_to, options)
924
938
 
925
- def _request(
926
- self,
927
- *,
928
- cast_to: Type[ResponseT],
929
- options: FinalRequestOptions,
930
- retries_taken: int,
931
- stream: bool,
932
- stream_cls: type[_StreamT] | None,
933
- ) -> ResponseT | _StreamT:
934
939
  # create a copy of the options we were given so that if the
935
940
  # options are mutated later & we then retry, the retries are
936
941
  # given the original options
937
942
  input_options = model_copy(options)
943
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
944
+ # ensure the idempotency key is reused between requests
945
+ input_options.idempotency_key = self._idempotency_key()
938
946
 
939
- cast_to = self._maybe_override_cast_to(cast_to, options)
940
- options = self._prepare_options(options)
947
+ response: httpx.Response | None = None
948
+ max_retries = input_options.get_max_retries(self.max_retries)
941
949
 
942
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
943
- request = self._build_request(options, retries_taken=retries_taken)
944
- self._prepare_request(request)
950
+ retries_taken = 0
951
+ for retries_taken in range(max_retries + 1):
952
+ options = model_copy(input_options)
953
+ options = self._prepare_options(options)
945
954
 
946
- kwargs: HttpxSendArgs = {}
947
- if self.custom_auth is not None:
948
- kwargs["auth"] = self.custom_auth
955
+ remaining_retries = max_retries - retries_taken
956
+ request = self._build_request(options, retries_taken=retries_taken)
957
+ self._prepare_request(request)
949
958
 
950
- log.debug("Sending HTTP Request: %s %s", request.method, request.url)
959
+ kwargs: HttpxSendArgs = {}
960
+ if self.custom_auth is not None:
961
+ kwargs["auth"] = self.custom_auth
951
962
 
952
- try:
953
- response = self._client.send(
954
- request,
955
- stream=stream or self._should_stream_response_body(request=request),
956
- **kwargs,
957
- )
958
- except httpx.TimeoutException as err:
959
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
963
+ if options.follow_redirects is not None:
964
+ kwargs["follow_redirects"] = options.follow_redirects
960
965
 
961
- if remaining_retries > 0:
962
- return self._retry_request(
963
- input_options,
964
- cast_to,
965
- retries_taken=retries_taken,
966
- stream=stream,
967
- stream_cls=stream_cls,
968
- response_headers=None,
969
- )
966
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
970
967
 
971
- log.debug("Raising timeout error")
972
- raise APITimeoutError(request=request) from err
973
- except Exception as err:
974
- log.debug("Encountered Exception", exc_info=True)
975
-
976
- if remaining_retries > 0:
977
- return self._retry_request(
978
- input_options,
979
- cast_to,
980
- retries_taken=retries_taken,
981
- stream=stream,
982
- stream_cls=stream_cls,
983
- response_headers=None,
968
+ response = None
969
+ try:
970
+ response = self._client.send(
971
+ request,
972
+ stream=stream or self._should_stream_response_body(request=request),
973
+ **kwargs,
984
974
  )
975
+ except httpx.TimeoutException as err:
976
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
977
+
978
+ if remaining_retries > 0:
979
+ self._sleep_for_retry(
980
+ retries_taken=retries_taken,
981
+ max_retries=max_retries,
982
+ options=input_options,
983
+ response=None,
984
+ )
985
+ continue
986
+
987
+ log.debug("Raising timeout error")
988
+ raise APITimeoutError(request=request) from err
989
+ except Exception as err:
990
+ log.debug("Encountered Exception", exc_info=True)
991
+
992
+ if remaining_retries > 0:
993
+ self._sleep_for_retry(
994
+ retries_taken=retries_taken,
995
+ max_retries=max_retries,
996
+ options=input_options,
997
+ response=None,
998
+ )
999
+ continue
1000
+
1001
+ log.debug("Raising connection error")
1002
+ raise APIConnectionError(request=request) from err
1003
+
1004
+ log.debug(
1005
+ 'HTTP Response: %s %s "%i %s" %s',
1006
+ request.method,
1007
+ request.url,
1008
+ response.status_code,
1009
+ response.reason_phrase,
1010
+ response.headers,
1011
+ )
985
1012
 
986
- log.debug("Raising connection error")
987
- raise APIConnectionError(request=request) from err
988
-
989
- log.debug(
990
- 'HTTP Response: %s %s "%i %s" %s',
991
- request.method,
992
- request.url,
993
- response.status_code,
994
- response.reason_phrase,
995
- response.headers,
996
- )
1013
+ try:
1014
+ response.raise_for_status()
1015
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1016
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1017
+
1018
+ if remaining_retries > 0 and self._should_retry(err.response):
1019
+ err.response.close()
1020
+ self._sleep_for_retry(
1021
+ retries_taken=retries_taken,
1022
+ max_retries=max_retries,
1023
+ options=input_options,
1024
+ response=response,
1025
+ )
1026
+ continue
997
1027
 
998
- try:
999
- response.raise_for_status()
1000
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1001
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1002
-
1003
- if remaining_retries > 0 and self._should_retry(err.response):
1004
- err.response.close()
1005
- return self._retry_request(
1006
- input_options,
1007
- cast_to,
1008
- retries_taken=retries_taken,
1009
- response_headers=err.response.headers,
1010
- stream=stream,
1011
- stream_cls=stream_cls,
1012
- )
1028
+ # If the response is streamed then we need to explicitly read the response
1029
+ # to completion before attempting to access the response text.
1030
+ if not err.response.is_closed:
1031
+ err.response.read()
1013
1032
 
1014
- # If the response is streamed then we need to explicitly read the response
1015
- # to completion before attempting to access the response text.
1016
- if not err.response.is_closed:
1017
- err.response.read()
1033
+ log.debug("Re-raising status error")
1034
+ raise self._make_status_error_from_response(err.response) from None
1018
1035
 
1019
- log.debug("Re-raising status error")
1020
- raise self._make_status_error_from_response(err.response) from None
1036
+ break
1021
1037
 
1038
+ assert response is not None, "could not resolve response (should never happen)"
1022
1039
  return self._process_response(
1023
1040
  cast_to=cast_to,
1024
1041
  options=options,
@@ -1028,37 +1045,20 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
1028
1045
  retries_taken=retries_taken,
1029
1046
  )
1030
1047
 
1031
- def _retry_request(
1032
- self,
1033
- options: FinalRequestOptions,
1034
- cast_to: Type[ResponseT],
1035
- *,
1036
- retries_taken: int,
1037
- response_headers: httpx.Headers | None,
1038
- stream: bool,
1039
- stream_cls: type[_StreamT] | None,
1040
- ) -> ResponseT | _StreamT:
1041
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
1048
+ def _sleep_for_retry(
1049
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
1050
+ ) -> None:
1051
+ remaining_retries = max_retries - retries_taken
1042
1052
  if remaining_retries == 1:
1043
1053
  log.debug("1 retry left")
1044
1054
  else:
1045
1055
  log.debug("%i retries left", remaining_retries)
1046
1056
 
1047
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
1057
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
1048
1058
  log.info("Retrying request to %s in %f seconds", options.url, timeout)
1049
1059
 
1050
- # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
1051
- # different thread if necessary.
1052
1060
  time.sleep(timeout)
1053
1061
 
1054
- return self._request(
1055
- options=options,
1056
- cast_to=cast_to,
1057
- retries_taken=retries_taken + 1,
1058
- stream=stream,
1059
- stream_cls=stream_cls,
1060
- )
1061
-
1062
1062
  def _process_response(
1063
1063
  self,
1064
1064
  *,
@@ -1071,7 +1071,14 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
1071
1071
  ) -> ResponseT:
1072
1072
  origin = get_origin(cast_to) or cast_to
1073
1073
 
1074
- if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
1074
+ if (
1075
+ inspect.isclass(origin)
1076
+ and issubclass(origin, BaseAPIResponse)
1077
+ # we only want to actually return the custom BaseAPIResponse class if we're
1078
+ # returning the raw response, or if we're not streaming SSE, as if we're streaming
1079
+ # SSE then `cast_to` doesn't actively reflect the type we need to parse into
1080
+ and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
1081
+ ):
1075
1082
  if not issubclass(origin, APIResponse):
1076
1083
  raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}")
1077
1084
 
@@ -1406,7 +1413,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1406
1413
  options: FinalRequestOptions,
1407
1414
  *,
1408
1415
  stream: Literal[False] = False,
1409
- remaining_retries: Optional[int] = None,
1410
1416
  ) -> ResponseT: ...
1411
1417
 
1412
1418
  @overload
@@ -1417,7 +1423,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1417
1423
  *,
1418
1424
  stream: Literal[True],
1419
1425
  stream_cls: type[_AsyncStreamT],
1420
- remaining_retries: Optional[int] = None,
1421
1426
  ) -> _AsyncStreamT: ...
1422
1427
 
1423
1428
  @overload
@@ -1428,7 +1433,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1428
1433
  *,
1429
1434
  stream: bool,
1430
1435
  stream_cls: type[_AsyncStreamT] | None = None,
1431
- remaining_retries: Optional[int] = None,
1432
1436
  ) -> ResponseT | _AsyncStreamT: ...
1433
1437
 
1434
1438
  async def request(
@@ -1438,116 +1442,114 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1438
1442
  *,
1439
1443
  stream: bool = False,
1440
1444
  stream_cls: type[_AsyncStreamT] | None = None,
1441
- remaining_retries: Optional[int] = None,
1442
- ) -> ResponseT | _AsyncStreamT:
1443
- if remaining_retries is not None:
1444
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
1445
- else:
1446
- retries_taken = 0
1447
-
1448
- return await self._request(
1449
- cast_to=cast_to,
1450
- options=options,
1451
- stream=stream,
1452
- stream_cls=stream_cls,
1453
- retries_taken=retries_taken,
1454
- )
1455
-
1456
- async def _request(
1457
- self,
1458
- cast_to: Type[ResponseT],
1459
- options: FinalRequestOptions,
1460
- *,
1461
- stream: bool,
1462
- stream_cls: type[_AsyncStreamT] | None,
1463
- retries_taken: int,
1464
1445
  ) -> ResponseT | _AsyncStreamT:
1465
1446
  if self._platform is None:
1466
1447
  # `get_platform` can make blocking IO calls so we
1467
1448
  # execute it earlier while we are in an async context
1468
1449
  self._platform = await asyncify(get_platform)()
1469
1450
 
1451
+ cast_to = self._maybe_override_cast_to(cast_to, options)
1452
+
1470
1453
  # create a copy of the options we were given so that if the
1471
1454
  # options are mutated later & we then retry, the retries are
1472
1455
  # given the original options
1473
1456
  input_options = model_copy(options)
1457
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
1458
+ # ensure the idempotency key is reused between requests
1459
+ input_options.idempotency_key = self._idempotency_key()
1474
1460
 
1475
- cast_to = self._maybe_override_cast_to(cast_to, options)
1476
- options = await self._prepare_options(options)
1461
+ response: httpx.Response | None = None
1462
+ max_retries = input_options.get_max_retries(self.max_retries)
1477
1463
 
1478
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
1479
- request = self._build_request(options, retries_taken=retries_taken)
1480
- await self._prepare_request(request)
1464
+ retries_taken = 0
1465
+ for retries_taken in range(max_retries + 1):
1466
+ options = model_copy(input_options)
1467
+ options = await self._prepare_options(options)
1481
1468
 
1482
- kwargs: HttpxSendArgs = {}
1483
- if self.custom_auth is not None:
1484
- kwargs["auth"] = self.custom_auth
1469
+ remaining_retries = max_retries - retries_taken
1470
+ request = self._build_request(options, retries_taken=retries_taken)
1471
+ await self._prepare_request(request)
1485
1472
 
1486
- try:
1487
- response = await self._client.send(
1488
- request,
1489
- stream=stream or self._should_stream_response_body(request=request),
1490
- **kwargs,
1491
- )
1492
- except httpx.TimeoutException as err:
1493
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
1473
+ kwargs: HttpxSendArgs = {}
1474
+ if self.custom_auth is not None:
1475
+ kwargs["auth"] = self.custom_auth
1494
1476
 
1495
- if remaining_retries > 0:
1496
- return await self._retry_request(
1497
- input_options,
1498
- cast_to,
1499
- retries_taken=retries_taken,
1500
- stream=stream,
1501
- stream_cls=stream_cls,
1502
- response_headers=None,
1503
- )
1477
+ if options.follow_redirects is not None:
1478
+ kwargs["follow_redirects"] = options.follow_redirects
1504
1479
 
1505
- log.debug("Raising timeout error")
1506
- raise APITimeoutError(request=request) from err
1507
- except Exception as err:
1508
- log.debug("Encountered Exception", exc_info=True)
1480
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
1509
1481
 
1510
- if remaining_retries > 0:
1511
- return await self._retry_request(
1512
- input_options,
1513
- cast_to,
1514
- retries_taken=retries_taken,
1515
- stream=stream,
1516
- stream_cls=stream_cls,
1517
- response_headers=None,
1482
+ response = None
1483
+ try:
1484
+ response = await self._client.send(
1485
+ request,
1486
+ stream=stream or self._should_stream_response_body(request=request),
1487
+ **kwargs,
1518
1488
  )
1489
+ except httpx.TimeoutException as err:
1490
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
1491
+
1492
+ if remaining_retries > 0:
1493
+ await self._sleep_for_retry(
1494
+ retries_taken=retries_taken,
1495
+ max_retries=max_retries,
1496
+ options=input_options,
1497
+ response=None,
1498
+ )
1499
+ continue
1500
+
1501
+ log.debug("Raising timeout error")
1502
+ raise APITimeoutError(request=request) from err
1503
+ except Exception as err:
1504
+ log.debug("Encountered Exception", exc_info=True)
1505
+
1506
+ if remaining_retries > 0:
1507
+ await self._sleep_for_retry(
1508
+ retries_taken=retries_taken,
1509
+ max_retries=max_retries,
1510
+ options=input_options,
1511
+ response=None,
1512
+ )
1513
+ continue
1514
+
1515
+ log.debug("Raising connection error")
1516
+ raise APIConnectionError(request=request) from err
1517
+
1518
+ log.debug(
1519
+ 'HTTP Response: %s %s "%i %s" %s',
1520
+ request.method,
1521
+ request.url,
1522
+ response.status_code,
1523
+ response.reason_phrase,
1524
+ response.headers,
1525
+ )
1519
1526
 
1520
- log.debug("Raising connection error")
1521
- raise APIConnectionError(request=request) from err
1527
+ try:
1528
+ response.raise_for_status()
1529
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1530
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1531
+
1532
+ if remaining_retries > 0 and self._should_retry(err.response):
1533
+ await err.response.aclose()
1534
+ await self._sleep_for_retry(
1535
+ retries_taken=retries_taken,
1536
+ max_retries=max_retries,
1537
+ options=input_options,
1538
+ response=response,
1539
+ )
1540
+ continue
1522
1541
 
1523
- log.debug(
1524
- 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
1525
- )
1542
+ # If the response is streamed then we need to explicitly read the response
1543
+ # to completion before attempting to access the response text.
1544
+ if not err.response.is_closed:
1545
+ await err.response.aread()
1526
1546
 
1527
- try:
1528
- response.raise_for_status()
1529
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1530
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1531
-
1532
- if remaining_retries > 0 and self._should_retry(err.response):
1533
- await err.response.aclose()
1534
- return await self._retry_request(
1535
- input_options,
1536
- cast_to,
1537
- retries_taken=retries_taken,
1538
- response_headers=err.response.headers,
1539
- stream=stream,
1540
- stream_cls=stream_cls,
1541
- )
1547
+ log.debug("Re-raising status error")
1548
+ raise self._make_status_error_from_response(err.response) from None
1542
1549
 
1543
- # If the response is streamed then we need to explicitly read the response
1544
- # to completion before attempting to access the response text.
1545
- if not err.response.is_closed:
1546
- await err.response.aread()
1547
-
1548
- log.debug("Re-raising status error")
1549
- raise self._make_status_error_from_response(err.response) from None
1550
+ break
1550
1551
 
1552
+ assert response is not None, "could not resolve response (should never happen)"
1551
1553
  return await self._process_response(
1552
1554
  cast_to=cast_to,
1553
1555
  options=options,
@@ -1557,35 +1559,20 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1557
1559
  retries_taken=retries_taken,
1558
1560
  )
1559
1561
 
1560
- async def _retry_request(
1561
- self,
1562
- options: FinalRequestOptions,
1563
- cast_to: Type[ResponseT],
1564
- *,
1565
- retries_taken: int,
1566
- response_headers: httpx.Headers | None,
1567
- stream: bool,
1568
- stream_cls: type[_AsyncStreamT] | None,
1569
- ) -> ResponseT | _AsyncStreamT:
1570
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
1562
+ async def _sleep_for_retry(
1563
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
1564
+ ) -> None:
1565
+ remaining_retries = max_retries - retries_taken
1571
1566
  if remaining_retries == 1:
1572
1567
  log.debug("1 retry left")
1573
1568
  else:
1574
1569
  log.debug("%i retries left", remaining_retries)
1575
1570
 
1576
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
1571
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
1577
1572
  log.info("Retrying request to %s in %f seconds", options.url, timeout)
1578
1573
 
1579
1574
  await anyio.sleep(timeout)
1580
1575
 
1581
- return await self._request(
1582
- options=options,
1583
- cast_to=cast_to,
1584
- retries_taken=retries_taken + 1,
1585
- stream=stream,
1586
- stream_cls=stream_cls,
1587
- )
1588
-
1589
1576
  async def _process_response(
1590
1577
  self,
1591
1578
  *,
@@ -1598,7 +1585,14 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1598
1585
  ) -> ResponseT:
1599
1586
  origin = get_origin(cast_to) or cast_to
1600
1587
 
1601
- if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
1588
+ if (
1589
+ inspect.isclass(origin)
1590
+ and issubclass(origin, BaseAPIResponse)
1591
+ # we only want to actually return the custom BaseAPIResponse class if we're
1592
+ # returning the raw response, or if we're not streaming SSE, as if we're streaming
1593
+ # SSE then `cast_to` doesn't actively reflect the type we need to parse into
1594
+ and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
1595
+ ):
1602
1596
  if not issubclass(origin, AsyncAPIResponse):
1603
1597
  raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}")
1604
1598
 
spitch/_client.py CHANGED
@@ -19,10 +19,7 @@ from ._types import (
19
19
  ProxiesTypes,
20
20
  RequestOptions,
21
21
  )
22
- from ._utils import (
23
- is_given,
24
- get_async_library,
25
- )
22
+ from ._utils import is_given, get_async_library
26
23
  from ._version import __version__
27
24
  from ._streaming import Stream as Stream, AsyncStream as AsyncStream
28
25
  from ._exceptions import SpitchError, APIStatusError
spitch/_models.py CHANGED
@@ -19,7 +19,6 @@ from typing_extensions import (
19
19
  )
20
20
 
21
21
  import pydantic
22
- import pydantic.generics
23
22
  from pydantic.fields import FieldInfo
24
23
 
25
24
  from ._types import (
@@ -623,8 +622,8 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
623
622
  # Note: if one variant defines an alias then they all should
624
623
  discriminator_alias = field_info.alias
625
624
 
626
- if field_info.annotation and is_literal_type(field_info.annotation):
627
- for entry in get_args(field_info.annotation):
625
+ if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation):
626
+ for entry in get_args(annotation):
628
627
  if isinstance(entry, str):
629
628
  mapping[entry] = variant
630
629
 
@@ -734,6 +733,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
734
733
  idempotency_key: str
735
734
  json_data: Body
736
735
  extra_json: AnyMapping
736
+ follow_redirects: bool
737
737
 
738
738
 
739
739
  @final
@@ -747,6 +747,7 @@ class FinalRequestOptions(pydantic.BaseModel):
747
747
  files: Union[HttpxRequestFiles, None] = None
748
748
  idempotency_key: Union[str, None] = None
749
749
  post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
750
+ follow_redirects: Union[bool, None] = None
750
751
 
751
752
  # It should be noted that we cannot use `json` here as that would override
752
753
  # a BaseModel method in an incompatible fashion.
spitch/_response.py CHANGED
@@ -227,7 +227,7 @@ class BaseAPIResponse(Generic[R]):
227
227
  # split is required to handle cases where additional information is included
228
228
  # in the response, e.g. application/json; charset=utf-8
229
229
  content_type, *_ = response.headers.get("content-type", "*").split(";")
230
- if content_type != "application/json":
230
+ if not content_type.endswith("json"):
231
231
  if is_basemodel(cast_to):
232
232
  try:
233
233
  data = response.json()
spitch/_types.py CHANGED
@@ -100,6 +100,7 @@ class RequestOptions(TypedDict, total=False):
100
100
  params: Query
101
101
  extra_json: AnyMapping
102
102
  idempotency_key: str
103
+ follow_redirects: bool
103
104
 
104
105
 
105
106
  # Sentinel class used until PEP 0661 is accepted
@@ -217,3 +218,4 @@ class _GenericAlias(Protocol):
217
218
 
218
219
  class HttpxSendArgs(TypedDict, total=False):
219
220
  auth: httpx.Auth
221
+ follow_redirects: bool
spitch/_utils/_proxy.py CHANGED
@@ -46,7 +46,10 @@ class LazyProxy(Generic[T], ABC):
46
46
  @property # type: ignore
47
47
  @override
48
48
  def __class__(self) -> type: # pyright: ignore
49
- proxied = self.__get_proxied__()
49
+ try:
50
+ proxied = self.__get_proxied__()
51
+ except Exception:
52
+ return type(self)
50
53
  if issubclass(type(proxied), LazyProxy):
51
54
  return type(proxied)
52
55
  return proxied.__class__
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+ from typing_extensions import override
5
+
6
+ from ._proxy import LazyProxy
7
+
8
+
9
+ class ResourcesProxy(LazyProxy[Any]):
10
+ """A proxy for the `spitch.resources` module.
11
+
12
+ This is used so that we can lazily import `spitch.resources` only when
13
+ needed *and* so that users can just import `spitch` and reference `spitch.resources`
14
+ """
15
+
16
+ @override
17
+ def __load__(self) -> Any:
18
+ import importlib
19
+
20
+ mod = importlib.import_module("spitch.resources")
21
+ return mod
22
+
23
+
24
+ resources = ResourcesProxy().__as_proxied__()
spitch/_utils/_typing.py CHANGED
@@ -81,7 +81,7 @@ def extract_type_var_from_base(
81
81
  ```
82
82
  """
83
83
  cls = cast(object, get_origin(typ) or typ)
84
- if cls in generic_bases:
84
+ if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains]
85
85
  # we're given the class directly
86
86
  return extract_type_arg(typ, index)
87
87
 
spitch/_utils/_utils.py CHANGED
@@ -72,8 +72,16 @@ def _extract_items(
72
72
  from .._files import assert_is_file_content
73
73
 
74
74
  # We have exhausted the path, return the entry we found.
75
- assert_is_file_content(obj, key=flattened_key)
76
75
  assert flattened_key is not None
76
+
77
+ if is_list(obj):
78
+ files: list[tuple[str, FileTypes]] = []
79
+ for entry in obj:
80
+ assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
81
+ files.append((flattened_key + "[]", cast(FileTypes, entry)))
82
+ return files
83
+
84
+ assert_is_file_content(obj, key=flattened_key)
77
85
  return [(flattened_key, cast(FileTypes, obj))]
78
86
 
79
87
  index += 1
spitch/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "spitch"
4
- __version__ = "1.25.0" # x-release-please-version
4
+ __version__ = "1.27.0" # x-release-please-version
@@ -9,12 +9,7 @@ import httpx
9
9
 
10
10
  from ..types import speech_generate_params, speech_transcribe_params
11
11
  from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
12
- from .._utils import (
13
- extract_files,
14
- maybe_transform,
15
- deepcopy_minimal,
16
- async_maybe_transform,
17
- )
12
+ from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
18
13
  from .._compat import cached_property
19
14
  from .._resource import SyncAPIResource, AsyncAPIResource
20
15
  from .._response import (
@@ -60,7 +55,7 @@ class SpeechResource(SyncAPIResource):
60
55
  def generate(
61
56
  self,
62
57
  *,
63
- language: Literal["yo", "en", "ha", "ig"],
58
+ language: Literal["yo", "en", "ha", "ig", "am"],
64
59
  text: str,
65
60
  voice: Literal[
66
61
  "sade",
@@ -81,6 +76,10 @@ class SpeechResource(SyncAPIResource):
81
76
  "jude",
82
77
  "henry",
83
78
  "kani",
79
+ "hana",
80
+ "selam",
81
+ "tena",
82
+ "tesfaye",
84
83
  ],
85
84
  stream: bool | NotGiven = NOT_GIVEN,
86
85
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -126,7 +125,7 @@ class SpeechResource(SyncAPIResource):
126
125
  def transcribe(
127
126
  self,
128
127
  *,
129
- language: Literal["yo", "en", "ha", "ig"],
128
+ language: Literal["yo", "en", "ha", "ig", "am"],
130
129
  content: Optional[FileTypes] | NotGiven = NOT_GIVEN,
131
130
  multispeaker: Optional[bool] | NotGiven = NOT_GIVEN,
132
131
  timestamp: Optional[bool] | NotGiven = NOT_GIVEN,
@@ -198,7 +197,7 @@ class AsyncSpeechResource(AsyncAPIResource):
198
197
  async def generate(
199
198
  self,
200
199
  *,
201
- language: Literal["yo", "en", "ha", "ig"],
200
+ language: Literal["yo", "en", "ha", "ig", "am"],
202
201
  text: str,
203
202
  voice: Literal[
204
203
  "sade",
@@ -219,6 +218,10 @@ class AsyncSpeechResource(AsyncAPIResource):
219
218
  "jude",
220
219
  "henry",
221
220
  "kani",
221
+ "hana",
222
+ "selam",
223
+ "tena",
224
+ "tesfaye",
222
225
  ],
223
226
  stream: bool | NotGiven = NOT_GIVEN,
224
227
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -264,7 +267,7 @@ class AsyncSpeechResource(AsyncAPIResource):
264
267
  async def transcribe(
265
268
  self,
266
269
  *,
267
- language: Literal["yo", "en", "ha", "ig"],
270
+ language: Literal["yo", "en", "ha", "ig", "am"],
268
271
  content: Optional[FileTypes] | NotGiven = NOT_GIVEN,
269
272
  multispeaker: Optional[bool] | NotGiven = NOT_GIVEN,
270
273
  timestamp: Optional[bool] | NotGiven = NOT_GIVEN,
spitch/resources/text.py CHANGED
@@ -8,10 +8,7 @@ import httpx
8
8
 
9
9
  from ..types import text_tone_mark_params, text_translate_params
10
10
  from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
11
- from .._utils import (
12
- maybe_transform,
13
- async_maybe_transform,
14
- )
11
+ from .._utils import maybe_transform, async_maybe_transform
15
12
  from .._compat import cached_property
16
13
  from .._resource import SyncAPIResource, AsyncAPIResource
17
14
  from .._response import (
@@ -50,7 +47,7 @@ class TextResource(SyncAPIResource):
50
47
  def tone_mark(
51
48
  self,
52
49
  *,
53
- language: Literal["yo", "en", "ha", "ig"],
50
+ language: Literal["yo", "en", "ha", "ig", "am"],
54
51
  text: str,
55
52
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
56
53
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -89,8 +86,8 @@ class TextResource(SyncAPIResource):
89
86
  def translate(
90
87
  self,
91
88
  *,
92
- source: Literal["yo", "en", "ha", "ig"],
93
- target: Literal["yo", "en", "ha", "ig"],
89
+ source: Literal["yo", "en", "ha", "ig", "am"],
90
+ target: Literal["yo", "en", "ha", "ig", "am"],
94
91
  text: str,
95
92
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
96
93
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -151,7 +148,7 @@ class AsyncTextResource(AsyncAPIResource):
151
148
  async def tone_mark(
152
149
  self,
153
150
  *,
154
- language: Literal["yo", "en", "ha", "ig"],
151
+ language: Literal["yo", "en", "ha", "ig", "am"],
155
152
  text: str,
156
153
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
157
154
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -190,8 +187,8 @@ class AsyncTextResource(AsyncAPIResource):
190
187
  async def translate(
191
188
  self,
192
189
  *,
193
- source: Literal["yo", "en", "ha", "ig"],
194
- target: Literal["yo", "en", "ha", "ig"],
190
+ source: Literal["yo", "en", "ha", "ig", "am"],
191
+ target: Literal["yo", "en", "ha", "ig", "am"],
195
192
  text: str,
196
193
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
197
194
  # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -8,7 +8,7 @@ __all__ = ["SpeechGenerateParams"]
8
8
 
9
9
 
10
10
  class SpeechGenerateParams(TypedDict, total=False):
11
- language: Required[Literal["yo", "en", "ha", "ig"]]
11
+ language: Required[Literal["yo", "en", "ha", "ig", "am"]]
12
12
 
13
13
  text: Required[str]
14
14
 
@@ -32,6 +32,10 @@ class SpeechGenerateParams(TypedDict, total=False):
32
32
  "jude",
33
33
  "henry",
34
34
  "kani",
35
+ "hana",
36
+ "selam",
37
+ "tena",
38
+ "tesfaye",
35
39
  ]
36
40
  ]
37
41
 
@@ -11,7 +11,7 @@ __all__ = ["SpeechTranscribeParams"]
11
11
 
12
12
 
13
13
  class SpeechTranscribeParams(TypedDict, total=False):
14
- language: Required[Literal["yo", "en", "ha", "ig"]]
14
+ language: Required[Literal["yo", "en", "ha", "ig", "am"]]
15
15
 
16
16
  content: Optional[FileTypes]
17
17
 
@@ -8,6 +8,6 @@ __all__ = ["TextToneMarkParams"]
8
8
 
9
9
 
10
10
  class TextToneMarkParams(TypedDict, total=False):
11
- language: Required[Literal["yo", "en", "ha", "ig"]]
11
+ language: Required[Literal["yo", "en", "ha", "ig", "am"]]
12
12
 
13
13
  text: Required[str]
@@ -8,8 +8,8 @@ __all__ = ["TextTranslateParams"]
8
8
 
9
9
 
10
10
  class TextTranslateParams(TypedDict, total=False):
11
- source: Required[Literal["yo", "en", "ha", "ig"]]
11
+ source: Required[Literal["yo", "en", "ha", "ig", "am"]]
12
12
 
13
- target: Required[Literal["yo", "en", "ha", "ig"]]
13
+ target: Required[Literal["yo", "en", "ha", "ig", "am"]]
14
14
 
15
15
  text: Required[str]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: spitch
3
- Version: 1.25.0
3
+ Version: 1.27.0
4
4
  Summary: The official Python library for the spitch API
5
5
  Project-URL: Homepage, https://github.com/spi-tch/spitch-python
6
6
  Project-URL: Repository, https://github.com/spi-tch/spitch-python
@@ -42,7 +42,7 @@ It is generated with [Stainless](https://www.stainless.com/).
42
42
 
43
43
  ## Documentation
44
44
 
45
- The REST API documentation can be found on [docs.spi-tch.com](https://docs.spi-tch.com). The full API of this library can be found in [api.md](https://github.com/spi-tch/spitch-python/tree/main/api.md).
45
+ The REST API documentation can be found on [docs.spitch.app](https://docs.spitch.app). The full API of this library can be found in [api.md](https://github.com/spi-tch/spitch-python/tree/main/api.md).
46
46
 
47
47
  ## Installation
48
48
 
@@ -1,40 +1,41 @@
1
- spitch/__init__.py,sha256=mBQhmNu88PeR66Zmw9atnzuU8f1mpSPcW7vn-EeSikE,2399
2
- spitch/_base_client.py,sha256=oORuab7liaQOg-Ud7teTgIfnBZVflIa8LfQA4BWTyog,65086
3
- spitch/_client.py,sha256=53FDDffn_8d-95rmM7-awLEUo9h_K2563fOmQm6qL-I,15451
1
+ spitch/__init__.py,sha256=Z4a7OtgmiVP34d5wa0YEk3g_4xYJ43k1mv47KD-eO_Y,2510
2
+ spitch/_base_client.py,sha256=3iXoiv3SggFtiMNIOMdmRtfI7peCQ08wowUoia6p89U,66013
3
+ spitch/_client.py,sha256=edWlodXy44ArTu9KY7M_zXYKMhs66gptWUqSuMwN0SI,15438
4
4
  spitch/_compat.py,sha256=fQkXUY7reJc8m_yguMWSjHBfO8lNzw4wOAxtkhP9d1Q,6607
5
5
  spitch/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
6
6
  spitch/_exceptions.py,sha256=xsQtKJTiIdz2X1bQDQFZcSW7WBofLazdQm9nMCyPEVM,3220
7
7
  spitch/_files.py,sha256=wV8OmI8oHeNVRtF-7aAEu22jtRG4FzjOioE8lBp-jNA,3617
8
- spitch/_models.py,sha256=hss4ZeAKZvg4pgBAlpn4iQ-IEERaInyjuGS3Nr8y5Rw,28892
8
+ spitch/_models.py,sha256=AFNOFV0-xr5GbImuRTYFjsaFtYV4NP7o_KLCHj82D-Y,28953
9
9
  spitch/_qs.py,sha256=AOkSz4rHtK4YI3ZU_kzea-zpwBUgEY8WniGmTPyEimc,4846
10
10
  spitch/_resource.py,sha256=TLFPcOOmtxZOQLh3XCNPB_BdrQpp0MIYoKoH52aRAu8,1100
11
- spitch/_response.py,sha256=3pGMe_eI_h4UPlyp8v6Xn_hu3Lv2i8KGnmKGZ1ZlMyc,28691
11
+ spitch/_response.py,sha256=-1LLK1wjPW3Hcro9NXjf_SnPRArU1ozdctNIStvxbWo,28690
12
12
  spitch/_streaming.py,sha256=5SpId2EIfF8Ee8UUYmJxqgHUGP1ZdHCUHhHCdNJREFA,10100
13
- spitch/_types.py,sha256=uuSZot9wXgdAMJzfF3raLmt3DvhThG7skqUC98_Dm1k,6167
14
- spitch/_version.py,sha256=ocswzkp59gFl77_040xvhERS-5cf1enlNPeIbnISDs4,159
13
+ spitch/_types.py,sha256=lccvqVi8E6-4SKt0rn1e9XXNePb0WwdDc10sPVSCygI,6221
14
+ spitch/_version.py,sha256=qnlIa9KJv95IyuCAR3qXA6wWbOpp0o1LVbRCaWmFzz4,159
15
15
  spitch/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  spitch/_utils/__init__.py,sha256=k266EatJr88V8Zseb7xUimTlCeno9SynRfLwadHP1b4,2016
17
17
  spitch/_utils/_logs.py,sha256=ApRyYK_WgZfEr_ygBUBXWMlTgeMr2tdNOGlH8jE4oJc,774
18
- spitch/_utils/_proxy.py,sha256=z3zsateHtb0EARTWKk8QZNHfPkqJbqwd1lM993LBwGE,1902
18
+ spitch/_utils/_proxy.py,sha256=aglnj2yBTDyGX9Akk2crZHrl10oqRmceUy2Zp008XEs,1975
19
19
  spitch/_utils/_reflection.py,sha256=ZmGkIgT_PuwedyNBrrKGbxoWtkpytJNU1uU4QHnmEMU,1364
20
+ spitch/_utils/_resources_proxy.py,sha256=O0A5203gv-9Tg2PzRAYDPjXvJg_FQoaN41auzUUY8NM,589
20
21
  spitch/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289
21
22
  spitch/_utils/_sync.py,sha256=TpGLrrhRNWTJtODNE6Fup3_k7zrWm1j2RlirzBwre-0,2862
22
23
  spitch/_utils/_transform.py,sha256=n7kskEWz6o__aoNvhFoGVyDoalNe6mJwp-g7BWkdj88,15617
23
- spitch/_utils/_typing.py,sha256=r5qmf4o7lLpjnklQ9UnQY6op6ipr7qq1GyLroPalH7g,3893
24
- spitch/_utils/_utils.py,sha256=8UmbPOy_AAr2uUjjFui-VZSrVBHRj6bfNEKRp5YZP2A,12004
24
+ spitch/_utils/_typing.py,sha256=9UuSEnmE7dgm1SG45Mt-ga1sBhnvZHpq3f2dXbhW1NM,3939
25
+ spitch/_utils/_utils.py,sha256=ts4CiiuNpFiGB6YMdkQRh2SZvYvsl7mAF-JWHCcLDf4,12312
25
26
  spitch/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
26
27
  spitch/resources/__init__.py,sha256=KT6rAvIlWHQk9QdM4Jp8ABziKILaBrrtiO7LCB5Wa5E,976
27
- spitch/resources/speech.py,sha256=LpI8rtw6xCuqEM_xBec9_owQAsPDRfeqH4azPzwnYWU,13011
28
- spitch/resources/text.py,sha256=0C2v6i5nawuwHkPbUo-J0RSrmMvAbKPmCPVwuLQTJmY,9657
28
+ spitch/resources/speech.py,sha256=wk-ZaTFNfiRVzdm5qOJd-MT88xoC39PWW9XVaAIV_Tw,13182
29
+ spitch/resources/text.py,sha256=wWIE7uyvthKfhkOtoodTBDv_0s6YgWgAqxwICquDSyo,9680
29
30
  spitch/types/__init__.py,sha256=xBZbzXwV3WlBdawp4Mb4IqoQG4VfaLbi-4FMqPbwqlE,704
30
- spitch/types/speech_generate_params.py,sha256=dBCEBNvo1f08VfT3zutoUEHwYGe5mSrr3-6qipTGJ_Y,799
31
- spitch/types/speech_transcribe_params.py,sha256=2nUgsYIURWTKx8qBYE0WfIHh6hXVcdgCz-UC1wIaXpA,515
31
+ spitch/types/speech_generate_params.py,sha256=u3rRJsJLrcw1FlJ-s_egy4-fYT8iz_jG5wbFho5xGjE,889
32
+ spitch/types/speech_transcribe_params.py,sha256=gEVjDl4T80QCE6C8zRmLSs0Addt0NlAwPENNYgkG4tQ,521
32
33
  spitch/types/speech_transcribe_response.py,sha256=mGNIIK-A_YOTi78tlYMRCKykPiFjSWm4gsRjPk1IvF8,516
33
- spitch/types/text_tone_mark_params.py,sha256=63P5VElxanYkDP1ZLEuQt97JSgVpMCaAo4WRWLDvhlY,349
34
+ spitch/types/text_tone_mark_params.py,sha256=MEnWzcSjPT_Z_8Mio96LgwYbx2BngG5By-MoeSt0Sms,355
34
35
  spitch/types/text_tone_mark_response.py,sha256=WGxZsBxLceZ03VM5dafZshp6azdDxpNHcJHhBX7A5DY,277
35
- spitch/types/text_translate_params.py,sha256=rU5hbTyBaP5L_akmgswHoNLcTKWN0Gz1kL1yt3EQL44,404
36
+ spitch/types/text_translate_params.py,sha256=skEeG7oTZUSl_gugnqL4Mb7HE_pEFhKNygZPTvci2hA,416
36
37
  spitch/types/text_translate_response.py,sha256=Az3QSpvarlCNTiB7uVzMH21YoWHWJMBEvgdKgVJZW4M,279
37
- spitch-1.25.0.dist-info/METADATA,sha256=S7-_2GNwJJJiBmJZZed3UIjekuCqxNkWpJkzvmNCDbQ,13255
38
- spitch-1.25.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
39
- spitch-1.25.0.dist-info/licenses/LICENSE,sha256=C0lDWY-no8IxmnqzQA9BA7Z8jeh_bogVPfeWSgeDDcc,11336
40
- spitch-1.25.0.dist-info/RECORD,,
38
+ spitch-1.27.0.dist-info/METADATA,sha256=IfBIUFzsGH5MPVuCD83uBffKuZP9-CfI-TTUH-JfqJY,13253
39
+ spitch-1.27.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
40
+ spitch-1.27.0.dist-info/licenses/LICENSE,sha256=C0lDWY-no8IxmnqzQA9BA7Z8jeh_bogVPfeWSgeDDcc,11336
41
+ spitch-1.27.0.dist-info/RECORD,,