* feat: support vertex as llm provider * fix * fix: add uv index-strategy to resolve dependency conflicts with pytorch index When using pytorch index for faster torch downloads in CI, filelock dependency resolution was failing because pytorch index only has older versions. Adding unsafe-best-match strategy allows uv to search all configured indexes. Also fix type checking warnings from ty. * fix: add index-strategy to root pyproject.toml for workspace-level uv resolution * chore: regenerate client SDKs after Vertex AI support
235 lines
7.7 KiB
Python
235 lines
7.7 KiB
Python
# coding: utf-8
|
|
|
|
"""
|
|
Hindsight HTTP API
|
|
|
|
HTTP API for Hindsight
|
|
|
|
The version of the OpenAPI document: 0.4.2
|
|
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
|
|
|
Do not edit the class manually.
|
|
""" # noqa: E501
|
|
|
|
|
|
import io
|
|
import json
|
|
import re
|
|
import ssl
|
|
from typing import Optional, Union
|
|
|
|
import aiohttp
|
|
import aiohttp_retry
|
|
|
|
from hindsight_client_api.exceptions import ApiException, ApiValueError
|
|
|
|
RESTResponseType = aiohttp.ClientResponse
|
|
|
|
ALLOW_RETRY_METHODS = frozenset({'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'TRACE'})
|
|
|
|
class RESTResponse(io.IOBase):
|
|
|
|
def __init__(self, resp) -> None:
|
|
self.response = resp
|
|
self.status = resp.status
|
|
self.reason = resp.reason
|
|
self.data = None
|
|
|
|
async def read(self):
|
|
if self.data is None:
|
|
self.data = await self.response.read()
|
|
return self.data
|
|
|
|
def getheaders(self):
|
|
"""Returns a CIMultiDictProxy of the response headers."""
|
|
return self.response.headers
|
|
|
|
def getheader(self, name, default=None):
|
|
"""Returns a given response header."""
|
|
return self.response.headers.get(name, default)
|
|
|
|
|
|
class RESTClientObject:
|
|
|
|
def __init__(self, configuration) -> None:
|
|
# Store configuration for deferred initialization
|
|
# aiohttp.TCPConnector requires a running event loop, so we defer
|
|
# creation until the first request (which runs in async context)
|
|
self._configuration = configuration
|
|
self._pool_manager: Optional[aiohttp.ClientSession] = None
|
|
self._retry_client: Optional[aiohttp_retry.RetryClient] = None
|
|
|
|
self.proxy = configuration.proxy
|
|
self.proxy_headers = configuration.proxy_headers
|
|
|
|
def _ensure_session(self) -> None:
|
|
"""Create aiohttp session lazily (must be called from async context)."""
|
|
if self._pool_manager is not None:
|
|
return
|
|
|
|
configuration = self._configuration
|
|
maxsize = configuration.connection_pool_maxsize
|
|
|
|
ssl_context = ssl.create_default_context(
|
|
cafile=configuration.ssl_ca_cert
|
|
)
|
|
if configuration.cert_file:
|
|
ssl_context.load_cert_chain(
|
|
configuration.cert_file, keyfile=configuration.key_file
|
|
)
|
|
|
|
if not configuration.verify_ssl:
|
|
ssl_context.check_hostname = False
|
|
ssl_context.verify_mode = ssl.CERT_NONE
|
|
|
|
connector = aiohttp.TCPConnector(
|
|
limit=maxsize,
|
|
ssl=ssl_context
|
|
)
|
|
|
|
self._pool_manager = aiohttp.ClientSession(
|
|
connector=connector,
|
|
trust_env=True
|
|
)
|
|
|
|
retries = configuration.retries
|
|
if retries is not None:
|
|
self._retry_client = aiohttp_retry.RetryClient(
|
|
client_session=self._pool_manager,
|
|
retry_options=aiohttp_retry.ExponentialRetry(
|
|
attempts=retries,
|
|
factor=2.0,
|
|
start_timeout=0.1,
|
|
max_timeout=120.0
|
|
)
|
|
)
|
|
|
|
@property
|
|
def pool_manager(self) -> aiohttp.ClientSession:
|
|
"""Get the pool manager, initializing if needed."""
|
|
self._ensure_session()
|
|
return self._pool_manager
|
|
|
|
@property
|
|
def retry_client(self) -> Optional[aiohttp_retry.RetryClient]:
|
|
"""Get the retry client, initializing if needed."""
|
|
self._ensure_session()
|
|
return self._retry_client
|
|
|
|
async def close(self):
|
|
if self._pool_manager is not None:
|
|
await self._pool_manager.close()
|
|
if self._retry_client is not None:
|
|
await self._retry_client.close()
|
|
|
|
async def request(
|
|
self,
|
|
method,
|
|
url,
|
|
headers=None,
|
|
body=None,
|
|
post_params=None,
|
|
_request_timeout=None
|
|
):
|
|
"""Execute request
|
|
|
|
:param method: http request method
|
|
:param url: http request url
|
|
:param headers: http request headers
|
|
:param body: request json body, for `application/json`
|
|
:param post_params: request post parameters,
|
|
`application/x-www-form-urlencoded`
|
|
and `multipart/form-data`
|
|
:param _request_timeout: timeout setting for this request. If one
|
|
number provided, it will be total request
|
|
timeout. It can also be a pair (tuple) of
|
|
(connection, read) timeouts.
|
|
"""
|
|
method = method.upper()
|
|
assert method in [
|
|
'GET',
|
|
'HEAD',
|
|
'DELETE',
|
|
'POST',
|
|
'PUT',
|
|
'PATCH',
|
|
'OPTIONS'
|
|
]
|
|
|
|
if post_params and body:
|
|
raise ApiValueError(
|
|
"body parameter cannot be used with post_params parameter."
|
|
)
|
|
|
|
post_params = post_params or {}
|
|
headers = headers or {}
|
|
# url already contains the URL query string
|
|
timeout = _request_timeout or 5 * 60
|
|
|
|
if 'Content-Type' not in headers:
|
|
headers['Content-Type'] = 'application/json'
|
|
|
|
args = {
|
|
"method": method,
|
|
"url": url,
|
|
"timeout": timeout,
|
|
"headers": headers
|
|
}
|
|
|
|
if self.proxy:
|
|
args["proxy"] = self.proxy
|
|
if self.proxy_headers:
|
|
args["proxy_headers"] = self.proxy_headers
|
|
|
|
# For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
|
|
if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
|
|
if re.search('json', headers['Content-Type'], re.IGNORECASE):
|
|
if body is not None:
|
|
body = json.dumps(body)
|
|
args["data"] = body
|
|
elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
|
|
args["data"] = aiohttp.FormData(post_params)
|
|
elif headers['Content-Type'] == 'multipart/form-data':
|
|
# must del headers['Content-Type'], or the correct
|
|
# Content-Type which generated by aiohttp
|
|
del headers['Content-Type']
|
|
data = aiohttp.FormData()
|
|
for param in post_params:
|
|
k, v = param
|
|
if isinstance(v, tuple) and len(v) == 3:
|
|
data.add_field(
|
|
k,
|
|
value=v[1],
|
|
filename=v[0],
|
|
content_type=v[2]
|
|
)
|
|
else:
|
|
# Ensures that dict objects are serialized
|
|
if isinstance(v, dict):
|
|
v = json.dumps(v)
|
|
elif isinstance(v, int):
|
|
v = str(v)
|
|
data.add_field(k, v)
|
|
args["data"] = data
|
|
|
|
# Pass a `bytes` or `str` parameter directly in the body to support
|
|
# other content types than Json when `body` argument is provided
|
|
# in serialized form
|
|
elif isinstance(body, str) or isinstance(body, bytes):
|
|
args["data"] = body
|
|
else:
|
|
# Cannot generate the request from given parameters
|
|
msg = """Cannot prepare a request message for provided
|
|
arguments. Please check that your arguments match
|
|
declared content type."""
|
|
raise ApiException(status=0, reason=msg)
|
|
|
|
pool_manager: Union[aiohttp.ClientSession, aiohttp_retry.RetryClient]
|
|
if self.retry_client is not None and method in ALLOW_RETRY_METHODS:
|
|
pool_manager = self.retry_client
|
|
else:
|
|
pool_manager = self.pool_manager
|
|
|
|
r = await pool_manager.request(**args)
|
|
|
|
return RESTResponse(r)
|