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 +5 -0
- spitch/_base_client.py +227 -233
- spitch/_client.py +1 -4
- spitch/_models.py +4 -3
- spitch/_response.py +1 -1
- spitch/_types.py +2 -0
- spitch/_utils/_proxy.py +4 -1
- spitch/_utils/_resources_proxy.py +24 -0
- spitch/_utils/_typing.py +1 -1
- spitch/_utils/_utils.py +9 -1
- spitch/_version.py +1 -1
- spitch/resources/speech.py +13 -10
- spitch/resources/text.py +7 -10
- spitch/types/speech_generate_params.py +5 -1
- spitch/types/speech_transcribe_params.py +1 -1
- spitch/types/text_tone_mark_params.py +1 -1
- spitch/types/text_translate_params.py +2 -2
- {spitch-1.25.0.dist-info → spitch-1.27.0.dist-info}/METADATA +2 -2
- {spitch-1.25.0.dist-info → spitch-1.27.0.dist-info}/RECORD +21 -20
- {spitch-1.25.0.dist-info → spitch-1.27.0.dist-info}/WHEEL +0 -0
- {spitch-1.25.0.dist-info → spitch-1.27.0.dist-info}/licenses/LICENSE +0 -0
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
|
|
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.
|
|
412
|
-
headers[idempotency_header] = options.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
|
-
|
|
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
|
-
|
|
940
|
-
|
|
947
|
+
response: httpx.Response | None = None
|
|
948
|
+
max_retries = input_options.get_max_retries(self.max_retries)
|
|
941
949
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
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
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
-
|
|
959
|
+
kwargs: HttpxSendArgs = {}
|
|
960
|
+
if self.custom_auth is not None:
|
|
961
|
+
kwargs["auth"] = self.custom_auth
|
|
951
962
|
|
|
952
|
-
|
|
953
|
-
|
|
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
|
-
|
|
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
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
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
|
-
|
|
1015
|
-
|
|
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
|
-
|
|
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
|
|
1032
|
-
self,
|
|
1033
|
-
|
|
1034
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
1476
|
-
|
|
1461
|
+
response: httpx.Response | None = None
|
|
1462
|
+
max_retries = input_options.get_max_retries(self.max_retries)
|
|
1477
1463
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
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
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
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
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
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
|
|
1496
|
-
|
|
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("
|
|
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
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
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
|
-
|
|
1521
|
-
|
|
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
|
-
|
|
1524
|
-
|
|
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
|
-
|
|
1528
|
-
|
|
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
|
-
|
|
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
|
|
1561
|
-
self,
|
|
1562
|
-
|
|
1563
|
-
|
|
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,
|
|
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
|
|
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
|
|
627
|
-
for entry in get_args(
|
|
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
|
|
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
|
-
|
|
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
spitch/resources/speech.py
CHANGED
|
@@ -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
|
|
|
@@ -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.
|
|
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.
|
|
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=
|
|
2
|
-
spitch/_base_client.py,sha256=
|
|
3
|
-
spitch/_client.py,sha256=
|
|
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=
|
|
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
|
|
11
|
+
spitch/_response.py,sha256=-1LLK1wjPW3Hcro9NXjf_SnPRArU1ozdctNIStvxbWo,28690
|
|
12
12
|
spitch/_streaming.py,sha256=5SpId2EIfF8Ee8UUYmJxqgHUGP1ZdHCUHhHCdNJREFA,10100
|
|
13
|
-
spitch/_types.py,sha256=
|
|
14
|
-
spitch/_version.py,sha256=
|
|
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=
|
|
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=
|
|
24
|
-
spitch/_utils/_utils.py,sha256=
|
|
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=
|
|
28
|
-
spitch/resources/text.py,sha256=
|
|
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=
|
|
31
|
-
spitch/types/speech_transcribe_params.py,sha256=
|
|
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=
|
|
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=
|
|
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.
|
|
38
|
-
spitch-1.
|
|
39
|
-
spitch-1.
|
|
40
|
-
spitch-1.
|
|
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,,
|
|
File without changes
|
|
File without changes
|