raccoonai 0.1.0a16__py3-none-any.whl → 0.1.0a18__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 raccoonai might be problematic. Click here for more details.
- raccoonai/__init__.py +5 -0
- raccoonai/_base_client.py +227 -233
- raccoonai/_client.py +1 -4
- raccoonai/_models.py +4 -3
- raccoonai/_response.py +1 -1
- raccoonai/_types.py +2 -0
- raccoonai/_utils/_proxy.py +4 -1
- raccoonai/_utils/_resources_proxy.py +24 -0
- raccoonai/_utils/_transform.py +46 -1
- raccoonai/_utils/_typing.py +3 -1
- raccoonai/_utils/_utils.py +9 -1
- raccoonai/_version.py +1 -1
- raccoonai/resources/fleet/extensions.py +1 -6
- raccoonai/resources/fleet/sessions.py +1 -4
- raccoonai/resources/lam/lam.py +3 -7
- raccoonai/resources/lam/tasks.py +1 -4
- raccoonai/resources/tail/apps.py +1 -4
- raccoonai/resources/tail/users.py +1 -4
- raccoonai/types/fleet/extension_get_response.py +0 -1
- raccoonai/types/fleet/extension_upload_response.py +0 -1
- raccoonai/types/fleet/session_logs_response.py +0 -1
- raccoonai/types/tail/user_create_response.py +0 -1
- {raccoonai-0.1.0a16.dist-info → raccoonai-0.1.0a18.dist-info}/METADATA +2 -3
- {raccoonai-0.1.0a16.dist-info → raccoonai-0.1.0a18.dist-info}/RECORD +26 -25
- {raccoonai-0.1.0a16.dist-info → raccoonai-0.1.0a18.dist-info}/WHEEL +0 -0
- {raccoonai-0.1.0a16.dist-info → raccoonai-0.1.0a18.dist-info}/licenses/LICENSE +0 -0
raccoonai/__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, Omit, NoneType, NotGiven, Transport, ProxiesTypes
|
|
5
7
|
from ._utils import file_from_path
|
|
@@ -80,6 +82,9 @@ __all__ = [
|
|
|
80
82
|
"DefaultAsyncHttpxClient",
|
|
81
83
|
]
|
|
82
84
|
|
|
85
|
+
if not _t.TYPE_CHECKING:
|
|
86
|
+
from ._utils._resources_proxy import resources as resources
|
|
87
|
+
|
|
83
88
|
_setup_logging()
|
|
84
89
|
|
|
85
90
|
# Update the __module__ attribute for exported symbols so that
|
raccoonai/_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
|
|
|
@@ -1402,7 +1409,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1402
1409
|
options: FinalRequestOptions,
|
|
1403
1410
|
*,
|
|
1404
1411
|
stream: Literal[False] = False,
|
|
1405
|
-
remaining_retries: Optional[int] = None,
|
|
1406
1412
|
) -> ResponseT: ...
|
|
1407
1413
|
|
|
1408
1414
|
@overload
|
|
@@ -1413,7 +1419,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1413
1419
|
*,
|
|
1414
1420
|
stream: Literal[True],
|
|
1415
1421
|
stream_cls: type[_AsyncStreamT],
|
|
1416
|
-
remaining_retries: Optional[int] = None,
|
|
1417
1422
|
) -> _AsyncStreamT: ...
|
|
1418
1423
|
|
|
1419
1424
|
@overload
|
|
@@ -1424,7 +1429,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1424
1429
|
*,
|
|
1425
1430
|
stream: bool,
|
|
1426
1431
|
stream_cls: type[_AsyncStreamT] | None = None,
|
|
1427
|
-
remaining_retries: Optional[int] = None,
|
|
1428
1432
|
) -> ResponseT | _AsyncStreamT: ...
|
|
1429
1433
|
|
|
1430
1434
|
async def request(
|
|
@@ -1434,116 +1438,114 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1434
1438
|
*,
|
|
1435
1439
|
stream: bool = False,
|
|
1436
1440
|
stream_cls: type[_AsyncStreamT] | None = None,
|
|
1437
|
-
remaining_retries: Optional[int] = None,
|
|
1438
|
-
) -> ResponseT | _AsyncStreamT:
|
|
1439
|
-
if remaining_retries is not None:
|
|
1440
|
-
retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
|
|
1441
|
-
else:
|
|
1442
|
-
retries_taken = 0
|
|
1443
|
-
|
|
1444
|
-
return await self._request(
|
|
1445
|
-
cast_to=cast_to,
|
|
1446
|
-
options=options,
|
|
1447
|
-
stream=stream,
|
|
1448
|
-
stream_cls=stream_cls,
|
|
1449
|
-
retries_taken=retries_taken,
|
|
1450
|
-
)
|
|
1451
|
-
|
|
1452
|
-
async def _request(
|
|
1453
|
-
self,
|
|
1454
|
-
cast_to: Type[ResponseT],
|
|
1455
|
-
options: FinalRequestOptions,
|
|
1456
|
-
*,
|
|
1457
|
-
stream: bool,
|
|
1458
|
-
stream_cls: type[_AsyncStreamT] | None,
|
|
1459
|
-
retries_taken: int,
|
|
1460
1441
|
) -> ResponseT | _AsyncStreamT:
|
|
1461
1442
|
if self._platform is None:
|
|
1462
1443
|
# `get_platform` can make blocking IO calls so we
|
|
1463
1444
|
# execute it earlier while we are in an async context
|
|
1464
1445
|
self._platform = await asyncify(get_platform)()
|
|
1465
1446
|
|
|
1447
|
+
cast_to = self._maybe_override_cast_to(cast_to, options)
|
|
1448
|
+
|
|
1466
1449
|
# create a copy of the options we were given so that if the
|
|
1467
1450
|
# options are mutated later & we then retry, the retries are
|
|
1468
1451
|
# given the original options
|
|
1469
1452
|
input_options = model_copy(options)
|
|
1453
|
+
if input_options.idempotency_key is None and input_options.method.lower() != "get":
|
|
1454
|
+
# ensure the idempotency key is reused between requests
|
|
1455
|
+
input_options.idempotency_key = self._idempotency_key()
|
|
1470
1456
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1457
|
+
response: httpx.Response | None = None
|
|
1458
|
+
max_retries = input_options.get_max_retries(self.max_retries)
|
|
1473
1459
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1460
|
+
retries_taken = 0
|
|
1461
|
+
for retries_taken in range(max_retries + 1):
|
|
1462
|
+
options = model_copy(input_options)
|
|
1463
|
+
options = await self._prepare_options(options)
|
|
1477
1464
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1465
|
+
remaining_retries = max_retries - retries_taken
|
|
1466
|
+
request = self._build_request(options, retries_taken=retries_taken)
|
|
1467
|
+
await self._prepare_request(request)
|
|
1481
1468
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
stream=stream or self._should_stream_response_body(request=request),
|
|
1486
|
-
**kwargs,
|
|
1487
|
-
)
|
|
1488
|
-
except httpx.TimeoutException as err:
|
|
1489
|
-
log.debug("Encountered httpx.TimeoutException", exc_info=True)
|
|
1469
|
+
kwargs: HttpxSendArgs = {}
|
|
1470
|
+
if self.custom_auth is not None:
|
|
1471
|
+
kwargs["auth"] = self.custom_auth
|
|
1490
1472
|
|
|
1491
|
-
if
|
|
1492
|
-
|
|
1493
|
-
input_options,
|
|
1494
|
-
cast_to,
|
|
1495
|
-
retries_taken=retries_taken,
|
|
1496
|
-
stream=stream,
|
|
1497
|
-
stream_cls=stream_cls,
|
|
1498
|
-
response_headers=None,
|
|
1499
|
-
)
|
|
1473
|
+
if options.follow_redirects is not None:
|
|
1474
|
+
kwargs["follow_redirects"] = options.follow_redirects
|
|
1500
1475
|
|
|
1501
|
-
log.debug("
|
|
1502
|
-
raise APITimeoutError(request=request) from err
|
|
1503
|
-
except Exception as err:
|
|
1504
|
-
log.debug("Encountered Exception", exc_info=True)
|
|
1476
|
+
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
|
|
1505
1477
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
stream_cls=stream_cls,
|
|
1513
|
-
response_headers=None,
|
|
1478
|
+
response = None
|
|
1479
|
+
try:
|
|
1480
|
+
response = await self._client.send(
|
|
1481
|
+
request,
|
|
1482
|
+
stream=stream or self._should_stream_response_body(request=request),
|
|
1483
|
+
**kwargs,
|
|
1514
1484
|
)
|
|
1485
|
+
except httpx.TimeoutException as err:
|
|
1486
|
+
log.debug("Encountered httpx.TimeoutException", exc_info=True)
|
|
1487
|
+
|
|
1488
|
+
if remaining_retries > 0:
|
|
1489
|
+
await self._sleep_for_retry(
|
|
1490
|
+
retries_taken=retries_taken,
|
|
1491
|
+
max_retries=max_retries,
|
|
1492
|
+
options=input_options,
|
|
1493
|
+
response=None,
|
|
1494
|
+
)
|
|
1495
|
+
continue
|
|
1496
|
+
|
|
1497
|
+
log.debug("Raising timeout error")
|
|
1498
|
+
raise APITimeoutError(request=request) from err
|
|
1499
|
+
except Exception as err:
|
|
1500
|
+
log.debug("Encountered Exception", exc_info=True)
|
|
1501
|
+
|
|
1502
|
+
if remaining_retries > 0:
|
|
1503
|
+
await self._sleep_for_retry(
|
|
1504
|
+
retries_taken=retries_taken,
|
|
1505
|
+
max_retries=max_retries,
|
|
1506
|
+
options=input_options,
|
|
1507
|
+
response=None,
|
|
1508
|
+
)
|
|
1509
|
+
continue
|
|
1510
|
+
|
|
1511
|
+
log.debug("Raising connection error")
|
|
1512
|
+
raise APIConnectionError(request=request) from err
|
|
1513
|
+
|
|
1514
|
+
log.debug(
|
|
1515
|
+
'HTTP Response: %s %s "%i %s" %s',
|
|
1516
|
+
request.method,
|
|
1517
|
+
request.url,
|
|
1518
|
+
response.status_code,
|
|
1519
|
+
response.reason_phrase,
|
|
1520
|
+
response.headers,
|
|
1521
|
+
)
|
|
1515
1522
|
|
|
1516
|
-
|
|
1517
|
-
|
|
1523
|
+
try:
|
|
1524
|
+
response.raise_for_status()
|
|
1525
|
+
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
|
|
1526
|
+
log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
|
|
1527
|
+
|
|
1528
|
+
if remaining_retries > 0 and self._should_retry(err.response):
|
|
1529
|
+
await err.response.aclose()
|
|
1530
|
+
await self._sleep_for_retry(
|
|
1531
|
+
retries_taken=retries_taken,
|
|
1532
|
+
max_retries=max_retries,
|
|
1533
|
+
options=input_options,
|
|
1534
|
+
response=response,
|
|
1535
|
+
)
|
|
1536
|
+
continue
|
|
1518
1537
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1538
|
+
# If the response is streamed then we need to explicitly read the response
|
|
1539
|
+
# to completion before attempting to access the response text.
|
|
1540
|
+
if not err.response.is_closed:
|
|
1541
|
+
await err.response.aread()
|
|
1522
1542
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
|
|
1526
|
-
log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
|
|
1527
|
-
|
|
1528
|
-
if remaining_retries > 0 and self._should_retry(err.response):
|
|
1529
|
-
await err.response.aclose()
|
|
1530
|
-
return await self._retry_request(
|
|
1531
|
-
input_options,
|
|
1532
|
-
cast_to,
|
|
1533
|
-
retries_taken=retries_taken,
|
|
1534
|
-
response_headers=err.response.headers,
|
|
1535
|
-
stream=stream,
|
|
1536
|
-
stream_cls=stream_cls,
|
|
1537
|
-
)
|
|
1543
|
+
log.debug("Re-raising status error")
|
|
1544
|
+
raise self._make_status_error_from_response(err.response) from None
|
|
1538
1545
|
|
|
1539
|
-
|
|
1540
|
-
# to completion before attempting to access the response text.
|
|
1541
|
-
if not err.response.is_closed:
|
|
1542
|
-
await err.response.aread()
|
|
1543
|
-
|
|
1544
|
-
log.debug("Re-raising status error")
|
|
1545
|
-
raise self._make_status_error_from_response(err.response) from None
|
|
1546
|
+
break
|
|
1546
1547
|
|
|
1548
|
+
assert response is not None, "could not resolve response (should never happen)"
|
|
1547
1549
|
return await self._process_response(
|
|
1548
1550
|
cast_to=cast_to,
|
|
1549
1551
|
options=options,
|
|
@@ -1553,35 +1555,20 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1553
1555
|
retries_taken=retries_taken,
|
|
1554
1556
|
)
|
|
1555
1557
|
|
|
1556
|
-
async def
|
|
1557
|
-
self,
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
*,
|
|
1561
|
-
retries_taken: int,
|
|
1562
|
-
response_headers: httpx.Headers | None,
|
|
1563
|
-
stream: bool,
|
|
1564
|
-
stream_cls: type[_AsyncStreamT] | None,
|
|
1565
|
-
) -> ResponseT | _AsyncStreamT:
|
|
1566
|
-
remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
|
|
1558
|
+
async def _sleep_for_retry(
|
|
1559
|
+
self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
|
|
1560
|
+
) -> None:
|
|
1561
|
+
remaining_retries = max_retries - retries_taken
|
|
1567
1562
|
if remaining_retries == 1:
|
|
1568
1563
|
log.debug("1 retry left")
|
|
1569
1564
|
else:
|
|
1570
1565
|
log.debug("%i retries left", remaining_retries)
|
|
1571
1566
|
|
|
1572
|
-
timeout = self._calculate_retry_timeout(remaining_retries, options,
|
|
1567
|
+
timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
|
|
1573
1568
|
log.info("Retrying request to %s in %f seconds", options.url, timeout)
|
|
1574
1569
|
|
|
1575
1570
|
await anyio.sleep(timeout)
|
|
1576
1571
|
|
|
1577
|
-
return await self._request(
|
|
1578
|
-
options=options,
|
|
1579
|
-
cast_to=cast_to,
|
|
1580
|
-
retries_taken=retries_taken + 1,
|
|
1581
|
-
stream=stream,
|
|
1582
|
-
stream_cls=stream_cls,
|
|
1583
|
-
)
|
|
1584
|
-
|
|
1585
1572
|
async def _process_response(
|
|
1586
1573
|
self,
|
|
1587
1574
|
*,
|
|
@@ -1594,7 +1581,14 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1594
1581
|
) -> ResponseT:
|
|
1595
1582
|
origin = get_origin(cast_to) or cast_to
|
|
1596
1583
|
|
|
1597
|
-
if
|
|
1584
|
+
if (
|
|
1585
|
+
inspect.isclass(origin)
|
|
1586
|
+
and issubclass(origin, BaseAPIResponse)
|
|
1587
|
+
# we only want to actually return the custom BaseAPIResponse class if we're
|
|
1588
|
+
# returning the raw response, or if we're not streaming SSE, as if we're streaming
|
|
1589
|
+
# SSE then `cast_to` doesn't actively reflect the type we need to parse into
|
|
1590
|
+
and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
|
|
1591
|
+
):
|
|
1598
1592
|
if not issubclass(origin, AsyncAPIResponse):
|
|
1599
1593
|
raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}")
|
|
1600
1594
|
|
raccoonai/_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 APIStatusError, RaccoonAIError
|
raccoonai/_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 (
|
|
@@ -627,8 +626,8 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
|
|
|
627
626
|
# Note: if one variant defines an alias then they all should
|
|
628
627
|
discriminator_alias = field_info.alias
|
|
629
628
|
|
|
630
|
-
if field_info
|
|
631
|
-
for entry in get_args(
|
|
629
|
+
if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation):
|
|
630
|
+
for entry in get_args(annotation):
|
|
632
631
|
if isinstance(entry, str):
|
|
633
632
|
mapping[entry] = variant
|
|
634
633
|
|
|
@@ -738,6 +737,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
|
|
|
738
737
|
idempotency_key: str
|
|
739
738
|
json_data: Body
|
|
740
739
|
extra_json: AnyMapping
|
|
740
|
+
follow_redirects: bool
|
|
741
741
|
|
|
742
742
|
|
|
743
743
|
@final
|
|
@@ -751,6 +751,7 @@ class FinalRequestOptions(pydantic.BaseModel):
|
|
|
751
751
|
files: Union[HttpxRequestFiles, None] = None
|
|
752
752
|
idempotency_key: Union[str, None] = None
|
|
753
753
|
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
|
|
754
|
+
follow_redirects: Union[bool, None] = None
|
|
754
755
|
|
|
755
756
|
# It should be noted that we cannot use `json` here as that would override
|
|
756
757
|
# a BaseModel method in an incompatible fashion.
|
raccoonai/_response.py
CHANGED
|
@@ -233,7 +233,7 @@ class BaseAPIResponse(Generic[R]):
|
|
|
233
233
|
# split is required to handle cases where additional information is included
|
|
234
234
|
# in the response, e.g. application/json; charset=utf-8
|
|
235
235
|
content_type, *_ = response.headers.get("content-type", "*").split(";")
|
|
236
|
-
if content_type
|
|
236
|
+
if not content_type.endswith("json"):
|
|
237
237
|
if is_basemodel(cast_to):
|
|
238
238
|
try:
|
|
239
239
|
data = response.json()
|
raccoonai/_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
|
|
@@ -215,3 +216,4 @@ class _GenericAlias(Protocol):
|
|
|
215
216
|
|
|
216
217
|
class HttpxSendArgs(TypedDict, total=False):
|
|
217
218
|
auth: httpx.Auth
|
|
219
|
+
follow_redirects: bool
|
raccoonai/_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 `raccoonai.resources` module.
|
|
11
|
+
|
|
12
|
+
This is used so that we can lazily import `raccoonai.resources` only when
|
|
13
|
+
needed *and* so that users can just import `raccoonai` and reference `raccoonai.resources`
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@override
|
|
17
|
+
def __load__(self) -> Any:
|
|
18
|
+
import importlib
|
|
19
|
+
|
|
20
|
+
mod = importlib.import_module("raccoonai.resources")
|
|
21
|
+
return mod
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
resources = ResourcesProxy().__as_proxied__()
|
raccoonai/_utils/_transform.py
CHANGED
|
@@ -5,13 +5,15 @@ import base64
|
|
|
5
5
|
import pathlib
|
|
6
6
|
from typing import Any, Mapping, TypeVar, cast
|
|
7
7
|
from datetime import date, datetime
|
|
8
|
-
from typing_extensions import Literal, get_args, override, get_type_hints
|
|
8
|
+
from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints
|
|
9
9
|
|
|
10
10
|
import anyio
|
|
11
11
|
import pydantic
|
|
12
12
|
|
|
13
13
|
from ._utils import (
|
|
14
14
|
is_list,
|
|
15
|
+
is_given,
|
|
16
|
+
lru_cache,
|
|
15
17
|
is_mapping,
|
|
16
18
|
is_iterable,
|
|
17
19
|
)
|
|
@@ -108,6 +110,7 @@ def transform(
|
|
|
108
110
|
return cast(_T, transformed)
|
|
109
111
|
|
|
110
112
|
|
|
113
|
+
@lru_cache(maxsize=8096)
|
|
111
114
|
def _get_annotated_type(type_: type) -> type | None:
|
|
112
115
|
"""If the given type is an `Annotated` type then it is returned, if not `None` is returned.
|
|
113
116
|
|
|
@@ -142,6 +145,10 @@ def _maybe_transform_key(key: str, type_: type) -> str:
|
|
|
142
145
|
return key
|
|
143
146
|
|
|
144
147
|
|
|
148
|
+
def _no_transform_needed(annotation: type) -> bool:
|
|
149
|
+
return annotation == float or annotation == int
|
|
150
|
+
|
|
151
|
+
|
|
145
152
|
def _transform_recursive(
|
|
146
153
|
data: object,
|
|
147
154
|
*,
|
|
@@ -184,6 +191,15 @@ def _transform_recursive(
|
|
|
184
191
|
return cast(object, data)
|
|
185
192
|
|
|
186
193
|
inner_type = extract_type_arg(stripped_type, 0)
|
|
194
|
+
if _no_transform_needed(inner_type):
|
|
195
|
+
# for some types there is no need to transform anything, so we can get a small
|
|
196
|
+
# perf boost from skipping that work.
|
|
197
|
+
#
|
|
198
|
+
# but we still need to convert to a list to ensure the data is json-serializable
|
|
199
|
+
if is_list(data):
|
|
200
|
+
return data
|
|
201
|
+
return list(data)
|
|
202
|
+
|
|
187
203
|
return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
|
|
188
204
|
|
|
189
205
|
if is_union_type(stripped_type):
|
|
@@ -245,6 +261,11 @@ def _transform_typeddict(
|
|
|
245
261
|
result: dict[str, object] = {}
|
|
246
262
|
annotations = get_type_hints(expected_type, include_extras=True)
|
|
247
263
|
for key, value in data.items():
|
|
264
|
+
if not is_given(value):
|
|
265
|
+
# we don't need to include `NotGiven` values here as they'll
|
|
266
|
+
# be stripped out before the request is sent anyway
|
|
267
|
+
continue
|
|
268
|
+
|
|
248
269
|
type_ = annotations.get(key)
|
|
249
270
|
if type_ is None:
|
|
250
271
|
# we do not have a type annotation for this field, leave it as is
|
|
@@ -332,6 +353,15 @@ async def _async_transform_recursive(
|
|
|
332
353
|
return cast(object, data)
|
|
333
354
|
|
|
334
355
|
inner_type = extract_type_arg(stripped_type, 0)
|
|
356
|
+
if _no_transform_needed(inner_type):
|
|
357
|
+
# for some types there is no need to transform anything, so we can get a small
|
|
358
|
+
# perf boost from skipping that work.
|
|
359
|
+
#
|
|
360
|
+
# but we still need to convert to a list to ensure the data is json-serializable
|
|
361
|
+
if is_list(data):
|
|
362
|
+
return data
|
|
363
|
+
return list(data)
|
|
364
|
+
|
|
335
365
|
return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
|
|
336
366
|
|
|
337
367
|
if is_union_type(stripped_type):
|
|
@@ -393,6 +423,11 @@ async def _async_transform_typeddict(
|
|
|
393
423
|
result: dict[str, object] = {}
|
|
394
424
|
annotations = get_type_hints(expected_type, include_extras=True)
|
|
395
425
|
for key, value in data.items():
|
|
426
|
+
if not is_given(value):
|
|
427
|
+
# we don't need to include `NotGiven` values here as they'll
|
|
428
|
+
# be stripped out before the request is sent anyway
|
|
429
|
+
continue
|
|
430
|
+
|
|
396
431
|
type_ = annotations.get(key)
|
|
397
432
|
if type_ is None:
|
|
398
433
|
# we do not have a type annotation for this field, leave it as is
|
|
@@ -400,3 +435,13 @@ async def _async_transform_typeddict(
|
|
|
400
435
|
else:
|
|
401
436
|
result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_)
|
|
402
437
|
return result
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@lru_cache(maxsize=8096)
|
|
441
|
+
def get_type_hints(
|
|
442
|
+
obj: Any,
|
|
443
|
+
globalns: dict[str, Any] | None = None,
|
|
444
|
+
localns: Mapping[str, Any] | None = None,
|
|
445
|
+
include_extras: bool = False,
|
|
446
|
+
) -> dict[str, Any]:
|
|
447
|
+
return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras)
|
raccoonai/_utils/_typing.py
CHANGED
|
@@ -13,6 +13,7 @@ from typing_extensions import (
|
|
|
13
13
|
get_origin,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
+
from ._utils import lru_cache
|
|
16
17
|
from .._types import InheritsGeneric
|
|
17
18
|
from .._compat import is_union as _is_union
|
|
18
19
|
|
|
@@ -66,6 +67,7 @@ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]:
|
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]]
|
|
70
|
+
@lru_cache(maxsize=8096)
|
|
69
71
|
def strip_annotated_type(typ: type) -> type:
|
|
70
72
|
if is_required_type(typ) or is_annotated_type(typ):
|
|
71
73
|
return strip_annotated_type(cast(type, get_args(typ)[0]))
|
|
@@ -108,7 +110,7 @@ def extract_type_var_from_base(
|
|
|
108
110
|
```
|
|
109
111
|
"""
|
|
110
112
|
cls = cast(object, get_origin(typ) or typ)
|
|
111
|
-
if cls in generic_bases:
|
|
113
|
+
if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains]
|
|
112
114
|
# we're given the class directly
|
|
113
115
|
return extract_type_arg(typ, index)
|
|
114
116
|
|
raccoonai/_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
|
raccoonai/_version.py
CHANGED
|
@@ -7,12 +7,7 @@ from typing import Mapping, cast
|
|
|
7
7
|
import httpx
|
|
8
8
|
|
|
9
9
|
from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
|
|
10
|
-
from ..._utils import
|
|
11
|
-
extract_files,
|
|
12
|
-
maybe_transform,
|
|
13
|
-
deepcopy_minimal,
|
|
14
|
-
async_maybe_transform,
|
|
15
|
-
)
|
|
10
|
+
from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
|
|
16
11
|
from ..._compat import cached_property
|
|
17
12
|
from ..._resource import SyncAPIResource, AsyncAPIResource
|
|
18
13
|
from ..._response import (
|
|
@@ -8,10 +8,7 @@ from typing_extensions import Literal
|
|
|
8
8
|
import httpx
|
|
9
9
|
|
|
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 (
|
raccoonai/resources/lam/lam.py
CHANGED
|
@@ -17,11 +17,7 @@ from .tasks import (
|
|
|
17
17
|
)
|
|
18
18
|
from ...types import lam_run_params
|
|
19
19
|
from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
|
|
20
|
-
from ..._utils import
|
|
21
|
-
required_args,
|
|
22
|
-
maybe_transform,
|
|
23
|
-
async_maybe_transform,
|
|
24
|
-
)
|
|
20
|
+
from ..._utils import required_args, maybe_transform, async_maybe_transform
|
|
25
21
|
from ..._compat import cached_property
|
|
26
22
|
from ..._resource import SyncAPIResource, AsyncAPIResource
|
|
27
23
|
from ..._response import (
|
|
@@ -266,7 +262,7 @@ class LamResource(SyncAPIResource):
|
|
|
266
262
|
"schema": schema,
|
|
267
263
|
"stream": stream,
|
|
268
264
|
},
|
|
269
|
-
lam_run_params.
|
|
265
|
+
lam_run_params.LamRunParamsStreaming if stream else lam_run_params.LamRunParamsNonStreaming,
|
|
270
266
|
),
|
|
271
267
|
options=make_request_options(
|
|
272
268
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
|
@@ -506,7 +502,7 @@ class AsyncLamResource(AsyncAPIResource):
|
|
|
506
502
|
"schema": schema,
|
|
507
503
|
"stream": stream,
|
|
508
504
|
},
|
|
509
|
-
lam_run_params.
|
|
505
|
+
lam_run_params.LamRunParamsStreaming if stream else lam_run_params.LamRunParamsNonStreaming,
|
|
510
506
|
),
|
|
511
507
|
options=make_request_options(
|
|
512
508
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
raccoonai/resources/lam/tasks.py
CHANGED
|
@@ -8,10 +8,7 @@ from typing_extensions import Literal
|
|
|
8
8
|
import httpx
|
|
9
9
|
|
|
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 (
|
raccoonai/resources/tail/apps.py
CHANGED
|
@@ -5,10 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import httpx
|
|
6
6
|
|
|
7
7
|
from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
|
|
8
|
-
from ..._utils import
|
|
9
|
-
maybe_transform,
|
|
10
|
-
async_maybe_transform,
|
|
11
|
-
)
|
|
8
|
+
from ..._utils import maybe_transform, async_maybe_transform
|
|
12
9
|
from ..._compat import cached_property
|
|
13
10
|
from ..._resource import SyncAPIResource, AsyncAPIResource
|
|
14
11
|
from ..._response import (
|
|
@@ -8,10 +8,7 @@ from typing_extensions import Literal
|
|
|
8
8
|
import httpx
|
|
9
9
|
|
|
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 (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: raccoonai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a18
|
|
4
4
|
Summary: The official Python library for the raccoonAI API
|
|
5
5
|
Project-URL: Homepage, https://github.com/raccoonaihq/raccoonai-python
|
|
6
6
|
Project-URL: Repository, https://github.com/raccoonaihq/raccoonai-python
|
|
@@ -162,7 +162,6 @@ response = client.lam.run(
|
|
|
162
162
|
raccoon_passcode="<end-user-raccoon-passcode>",
|
|
163
163
|
advanced={
|
|
164
164
|
"block_ads": True,
|
|
165
|
-
"extension_ids": ["df2399ea-a938-438f-9d4b-ef3bc95cf8af"],
|
|
166
165
|
"proxy": {
|
|
167
166
|
"city": "sanfrancisco",
|
|
168
167
|
"country": "us",
|
|
@@ -177,7 +176,7 @@ print(response.advanced)
|
|
|
177
176
|
|
|
178
177
|
## File uploads
|
|
179
178
|
|
|
180
|
-
Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
|
|
179
|
+
Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
|
|
181
180
|
|
|
182
181
|
```python
|
|
183
182
|
from pathlib import Path
|
|
@@ -1,53 +1,54 @@
|
|
|
1
|
-
raccoonai/__init__.py,sha256
|
|
2
|
-
raccoonai/_base_client.py,sha256=
|
|
3
|
-
raccoonai/_client.py,sha256
|
|
1
|
+
raccoonai/__init__.py,sha256=-A6AJxDjviQFqkzoBiO5BzSEe_LEMi5pQ861niFmLtk,2634
|
|
2
|
+
raccoonai/_base_client.py,sha256=R9UA9AEmUXbU3sPAQ8759ehYh9UAW640OGZ356drsTA,65887
|
|
3
|
+
raccoonai/_client.py,sha256=b4Du_3dCdsg2TTDuMwokhvEDTrn5CWy5bqcsubr2ZaY,18886
|
|
4
4
|
raccoonai/_compat.py,sha256=VWemUKbj6DDkQ-O4baSpHVLJafotzeXmCQGJugfVTIw,6580
|
|
5
5
|
raccoonai/_constants.py,sha256=FkmVVcfVS3gR69v_fTrqA_qjakyxJHOWJcw3jpEck8Y,465
|
|
6
6
|
raccoonai/_exceptions.py,sha256=Y-DcD2M8xkSw8IEkk4KHj73O8GQxCtWm4HWYQ02j7z8,3226
|
|
7
7
|
raccoonai/_files.py,sha256=a0SHeBu6FT5rt_CKotWZyna5GpgB42go35AUK5sEiD4,3624
|
|
8
|
-
raccoonai/_models.py,sha256=
|
|
8
|
+
raccoonai/_models.py,sha256=G1vczEodX0vUySeVKbF-mbzlaObNL1oVAYH4c65agRk,29131
|
|
9
9
|
raccoonai/_qs.py,sha256=AOkSz4rHtK4YI3ZU_kzea-zpwBUgEY8WniGmTPyEimc,4846
|
|
10
10
|
raccoonai/_resource.py,sha256=zfxyYCvzutc1dvCP-j9UPc1sn9U8F-X9gGyqleOvCxY,1118
|
|
11
|
-
raccoonai/_response.py,sha256=
|
|
11
|
+
raccoonai/_response.py,sha256=RuSNonzoPj-1q1FxDHtgUIMLvTqKLTg8GtTxrNV3XzY,28806
|
|
12
12
|
raccoonai/_streaming.py,sha256=zHnkREZO5v33YJ7P0YZ7KhJET4ZzevGw1JzRY2-Mls4,10112
|
|
13
|
-
raccoonai/_types.py,sha256=
|
|
14
|
-
raccoonai/_version.py,sha256=
|
|
13
|
+
raccoonai/_types.py,sha256=nkB-Qzpl6R51fi6nwZsLH73N43GgXxZqG3KJ51pm8-o,6200
|
|
14
|
+
raccoonai/_version.py,sha256=pjqr1cd84GFAJEhrOZvHafZ9dAJuD3dgemPBXa1ZGj4,170
|
|
15
15
|
raccoonai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
raccoonai/_utils/__init__.py,sha256=PNZ_QJuzZEgyYXqkO1HVhGkj5IU9bglVUcw7H-Knjzw,2062
|
|
17
17
|
raccoonai/_utils/_logs.py,sha256=Af3FKkE-LAPzYTl8bnFD4yPvPBIO-QyCra-r9_dSmOM,784
|
|
18
|
-
raccoonai/_utils/_proxy.py,sha256=
|
|
18
|
+
raccoonai/_utils/_proxy.py,sha256=aglnj2yBTDyGX9Akk2crZHrl10oqRmceUy2Zp008XEs,1975
|
|
19
19
|
raccoonai/_utils/_reflection.py,sha256=ZmGkIgT_PuwedyNBrrKGbxoWtkpytJNU1uU4QHnmEMU,1364
|
|
20
|
+
raccoonai/_utils/_resources_proxy.py,sha256=rlNyHdV3OTAHOl9OHEyoMVhEN_mlzxq-Wb9Hz_P-Hxo,604
|
|
20
21
|
raccoonai/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289
|
|
21
22
|
raccoonai/_utils/_sync.py,sha256=TpGLrrhRNWTJtODNE6Fup3_k7zrWm1j2RlirzBwre-0,2862
|
|
22
|
-
raccoonai/_utils/_transform.py,sha256=
|
|
23
|
-
raccoonai/_utils/_typing.py,sha256=
|
|
24
|
-
raccoonai/_utils/_utils.py,sha256=
|
|
23
|
+
raccoonai/_utils/_transform.py,sha256=n7kskEWz6o__aoNvhFoGVyDoalNe6mJwp-g7BWkdj88,15617
|
|
24
|
+
raccoonai/_utils/_typing.py,sha256=D0DbbNu8GnYQTSICnTSHDGsYXj8TcAKyhejb0XcnjtY,4602
|
|
25
|
+
raccoonai/_utils/_utils.py,sha256=ts4CiiuNpFiGB6YMdkQRh2SZvYvsl7mAF-JWHCcLDf4,12312
|
|
25
26
|
raccoonai/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
|
|
26
27
|
raccoonai/resources/__init__.py,sha256=J3rNFGIi-0n5C4uqUHDax-dZCtNhFxnz1_LiF1Hm498,1374
|
|
27
28
|
raccoonai/resources/fleet/__init__.py,sha256=vGB3Zi0TL72b8i88mnBm-evtTa2DMGqZAQhdfMis6tQ,1517
|
|
28
|
-
raccoonai/resources/fleet/extensions.py,sha256=
|
|
29
|
+
raccoonai/resources/fleet/extensions.py,sha256=xHaXfCLLki5dSHJNoZG0834X_efayg5uJBXDGw2Io6c,15297
|
|
29
30
|
raccoonai/resources/fleet/fleet.py,sha256=q8Ola3g7CQOMO0XQ9CDsQ0iG3n7FEnUe_4VJLGGlGfw,4818
|
|
30
|
-
raccoonai/resources/fleet/sessions.py,sha256=
|
|
31
|
+
raccoonai/resources/fleet/sessions.py,sha256=d-EmJr0PnmaxGuM2b7W7CVPWIDPj7LAxXCJGLMWq6vY,28527
|
|
31
32
|
raccoonai/resources/lam/__init__.py,sha256=Thj6eBm5r07sqkqyE9K1JGjNKyzgaodJLy-qNrfrCWc,950
|
|
32
|
-
raccoonai/resources/lam/lam.py,sha256=
|
|
33
|
-
raccoonai/resources/lam/tasks.py,sha256=
|
|
33
|
+
raccoonai/resources/lam/lam.py,sha256=9QYNhhYDX1wUn6DsRPgjqhx-ikpfhy_uGlqQGbAoeHM,22438
|
|
34
|
+
raccoonai/resources/lam/tasks.py,sha256=K71XB-13oyb9_6QODgGFDf1BmWtgUZR6DZrz1CdcS2w,12529
|
|
34
35
|
raccoonai/resources/tail/__init__.py,sha256=1iOxKP4PRjPGjU1ss2L_8YbwPZ_MIA5cnyFbyELeRoA,1387
|
|
35
|
-
raccoonai/resources/tail/apps.py,sha256=
|
|
36
|
+
raccoonai/resources/tail/apps.py,sha256=SqxIPZ6M-f9pZl0bJezFi2HhqecmZ14NIfti94OqkPM,7995
|
|
36
37
|
raccoonai/resources/tail/tail.py,sha256=L3HtDA-G4GSGdD5YjWp5IcjuYGINRFdua0-YKHrAavA,4517
|
|
37
|
-
raccoonai/resources/tail/users.py,sha256=
|
|
38
|
+
raccoonai/resources/tail/users.py,sha256=zlnmF5ycgv47dA3Zwj7ryADicsPw5pu3666kzmN7UgE,14490
|
|
38
39
|
raccoonai/types/__init__.py,sha256=zPr9s--p1dwMuN9TZf38xnrr_T0G8FA2LLIPUazW4FA,243
|
|
39
40
|
raccoonai/types/lam_run_params.py,sha256=WEz4-fSyhk72lke57m68n0ATFeiHwpOsaWvnguoz6yM,2733
|
|
40
41
|
raccoonai/types/lam_run_response.py,sha256=pOBB0xmGZou7vMG-dmhUk6v5pMyJF4dXWnNWXAHvfW0,891
|
|
41
42
|
raccoonai/types/fleet/__init__.py,sha256=4z1oeJkLJ-abdHCDxQ0lO9zOkpJXtSaR5qgVeVsaHLE,1101
|
|
42
43
|
raccoonai/types/fleet/extension_all_response.py,sha256=J9Y5qq3Eukik_0Bmyq75kLEhJJohJOF_Hx8vWUv_Sq8,527
|
|
43
|
-
raccoonai/types/fleet/extension_get_response.py,sha256=
|
|
44
|
+
raccoonai/types/fleet/extension_get_response.py,sha256=MXYmru1ocw_-VtUQHGKYU5wOJ6QST5NzXfwPxl4wb7w,397
|
|
44
45
|
raccoonai/types/fleet/extension_upload_params.py,sha256=UUR2H0DrvNgpLJfCTyO28Gv5xI8EEOEFErwmj3Ru5z0,328
|
|
45
|
-
raccoonai/types/fleet/extension_upload_response.py,sha256=
|
|
46
|
+
raccoonai/types/fleet/extension_upload_response.py,sha256=IDWmw40WxBfK8aZe2SgtZY4oEi-N1kSV7ZpjvCtdg24,403
|
|
46
47
|
raccoonai/types/fleet/session_all_params.py,sha256=8PmXYnnvdc52Hw4q4d0bzWMeeq_7m5LraRghHwI_wl0,1432
|
|
47
48
|
raccoonai/types/fleet/session_all_response.py,sha256=Z4_iJCGAtfFpYZTiBmYAZJaPzyVUfS1Qkwe5nEeuPcY,1721
|
|
48
49
|
raccoonai/types/fleet/session_create_params.py,sha256=1DK11wJV9XqXRRIHPuHMpusxQeg3ueM1a5NcAaQOoZA,2567
|
|
49
50
|
raccoonai/types/fleet/session_create_response.py,sha256=WbVwyfesj9QWuYEhDPoNji7U5eJmEOA44NmImWCl4qk,602
|
|
50
|
-
raccoonai/types/fleet/session_logs_response.py,sha256
|
|
51
|
+
raccoonai/types/fleet/session_logs_response.py,sha256=-Qzhml3YEMUFMbsTlFX8OqA0qPOX040CO3U4LaQlweU,364
|
|
51
52
|
raccoonai/types/fleet/session_media_response.py,sha256=FsE4vgxweb0y25J5YYa_fHbVy8IaQfstEZ90QIxVx3c,1118
|
|
52
53
|
raccoonai/types/fleet/session_status_response.py,sha256=bmmZk4f6aV3lS3ENILoLQm38dKVkVY4uN-H-OTUrxgM,432
|
|
53
54
|
raccoonai/types/fleet/session_terminate_response.py,sha256=UbA_9CwZOmRMcxqaNMIFNGA8pwpAY1fagb_2voYBQ0c,438
|
|
@@ -62,10 +63,10 @@ raccoonai/types/tail/app_linked_response.py,sha256=i7ox4Z5UsJhQMQm8DkB9YTz9chqNs
|
|
|
62
63
|
raccoonai/types/tail/user_all_params.py,sha256=SeS6QCT2HYtQnUFaptIcy2Fla01CQq0deno72yb8TS8,832
|
|
63
64
|
raccoonai/types/tail/user_all_response.py,sha256=J13hVLXOWD_S5AWs--i4fR73FUuiJ8cZQ9UoBIkPVRo,1227
|
|
64
65
|
raccoonai/types/tail/user_create_params.py,sha256=4Pe2d2L_TqNAeBNtG92ml1zLA3wTt_NE_6zJ-2YDtFY,370
|
|
65
|
-
raccoonai/types/tail/user_create_response.py,sha256=
|
|
66
|
+
raccoonai/types/tail/user_create_response.py,sha256=eYexuFeSkGZs58pqSLga-9D4zAwCEiX7xi6OllB3F2Q,517
|
|
66
67
|
raccoonai/types/tail/user_status_params.py,sha256=gxSN0_zGeqOSKpDaoKh0O6l8j0jMyxP7f7ONIPWOVZE,459
|
|
67
68
|
raccoonai/types/tail/user_status_response.py,sha256=BakDkr-yhueamBAx-wrHCUZ4h9rYJCQPnDD49paA_aU,472
|
|
68
|
-
raccoonai-0.1.
|
|
69
|
-
raccoonai-0.1.
|
|
70
|
-
raccoonai-0.1.
|
|
71
|
-
raccoonai-0.1.
|
|
69
|
+
raccoonai-0.1.0a18.dist-info/METADATA,sha256=Vo2rhDy5UXTSIh1O7Lk1vFG-XssKltyrMsHsW8koh2c,15461
|
|
70
|
+
raccoonai-0.1.0a18.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
71
|
+
raccoonai-0.1.0a18.dist-info/licenses/LICENSE,sha256=enGvZ2fGU7wGgMPWkgyWhnsFhCpxwdeG_selO_ovoTM,11340
|
|
72
|
+
raccoonai-0.1.0a18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|