Skip to content

Commit

Permalink
publish v0.0.5x
Browse files Browse the repository at this point in the history
  • Loading branch information
trisongz committed Feb 2, 2024
1 parent 7fd012a commit 1bc9ab0
Show file tree
Hide file tree
Showing 16 changed files with 2,983 additions and 29 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ __pycache__*
/.idea/
async_openai/v1*
tests/private_*
!tests/
!tests/
tests/v2/private_*
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelogs

#### v0.0.50 (2024-02-01)

**Breaking Changes**

- The `OpenAI` client has been refactored to be a singleton `ProxyObject` vs a `Type` object.

Currently, this API is accessible with `async_openai.OpenAIManager`, which provides all the existing functionality of the `OpenAI` client, with a few additional features.

- `OpenAIManager` supports automatic proxy rotation and client selection based on available models.

- `OpenAIManager` supports automatic retrying of failed requests, as well as enabling automatic healthchecking prior to each request to ensure the endpoint is available with `auto_healthcheck_enabled`, otherwise it will rotate to another endpoint. This is useful for ensuring high availability and reliability of the API.

Future versions will deprecate the `OpenAI` client in favor of the `OpenAIManager` object.

- Added new `OpenAIFunctions` class which provides a robust interface for creating and running functions. This class is also a singleton `ProxyObject`.

This can be accessed through the `OpenAIManager.functions` object


#### v0.0.41 (2023-11-06)

Expand Down
2 changes: 1 addition & 1 deletion async_openai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@


from async_openai.routes import ApiRoutes
from async_openai.client import OpenAIClient, OpenAI
from async_openai.client import OpenAIClient, OpenAI, OpenAIManager



Expand Down
84 changes: 80 additions & 4 deletions async_openai/client.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import aiohttpx
import contextlib
from typing import Optional, Callable, Dict, Union, List

from async_openai.schemas import *
from async_openai.types.options import ApiType
from async_openai.utils.logs import logger
from async_openai.utils.config import get_settings, OpenAISettings, AzureOpenAISettings, OpenAIAuth
from async_openai.utils.config import get_settings, OpenAISettings, AzureOpenAISettings, OpenAIAuth, ProxyObject
from async_openai.routes import ApiRoutes
from async_openai.meta import OpenAIMetaClass

from async_openai.manager import OpenAIManager as OpenAISessionManager

_update_params = [
'url',
Expand Down Expand Up @@ -72,8 +73,42 @@ def __init__(
"""
Lazily Instantiates the OpenAI Client
"""
self.model_rate_limits: Dict[str, Dict[str, int]] = {}
self.client_callbacks: List[Callable] = []
self.configure_params(**kwargs)

def response_event_hook(self, response: aiohttpx.Response):
"""
Monitor the rate limits
"""
url = response.url
headers = response.headers
with contextlib.suppress(Exception):
if self.is_azure:
model_name = str(url).split('deployments/', 1)[-1].split('/', 1)[0].strip()
else:
model_name = headers.get('openai-model')
model_name = model_name.lstrip("https:").strip()
if not model_name: return
if model_name not in self.model_rate_limits:
self.model_rate_limits[model_name] = {}
for key, value in {
'x-ratelimit-remaining-requests': 'remaining',
'x-ratelimit-remaining-tokens': 'remaining_tokens',
'x-ratelimit-limit-tokens': 'limit_tokens',
'x-ratelimit-limit-requests': 'limit_requests',
}.items():
if key in headers:
self.model_rate_limits[model_name][value] = int(headers[key])
if self.debug_enabled:
logger.info(f"Rate Limits: {self.model_rate_limits}")

async def aresponse_event_hook(self, response: aiohttpx.Response):
"""
Monitor the rate limits
"""
return self.response_event_hook(response)

@property
def client(self) -> aiohttpx.Client:
"""
Expand Down Expand Up @@ -121,6 +156,7 @@ def configure_params(
is_azure: Optional[bool] = None,
azure_model_mapping: Optional[Dict[str, str]] = None,
auth: Optional[OpenAIAuth] = None,
client_callbacks: Optional[List[Callable]] = None,
**kwargs
): # sourcery skip: low-code-quality
"""
Expand Down Expand Up @@ -233,6 +269,9 @@ def configure_params(
self.log_method = logger.info if self.debug_enabled else logger.debug
if not self.debug_enabled:
self.settings.disable_httpx_logger()

if client_callbacks is not None:
self.client_callbacks = client_callbacks
# if self.debug_enabled:
# logger.info(f"OpenAI Client Configured: {self.client.base_url}")
# logger.debug(f"Debug Enabled: {self.debug_enabled}")
Expand All @@ -243,13 +282,18 @@ def configure_client(self, **kwargs):
"""
if self._client is not None: return
# logger.info(f"OpenAI Client Configured: {self.base_url} [{self.name}]")
extra_kwargs = {}
if self.settings.limit_monitor_enabled:
extra_kwargs['event_hooks'] = {'response': [self.response_event_hook]}
extra_kwargs['async_event_hooks'] = {'response': [self.aresponse_event_hook]}

self._client = aiohttpx.Client(
base_url = self.base_url,
timeout = self.timeout,
limits = self.settings.api_client_limits,
auth = self.auth,
headers = self.headers,
# auth = self.settings.
**extra_kwargs,
)

def configure_routes(self, **kwargs):
Expand All @@ -273,6 +317,7 @@ def configure_routes(self, **kwargs):
azure_model_mapping = self.azure_model_mapping,
disable_retries = self.disable_retries,
retry_function = self.retry_function,
client_callbacks = self.client_callbacks,
**kwargs
)
if self.debug_enabled:
Expand Down Expand Up @@ -369,11 +414,42 @@ async def __aexit__(self, exc_type, exc_value, traceback):
await self.async_close()


def ping(self, timeout: Optional[float] = 1.0) -> bool:
"""
Pings the API Endpoint to check if it's alive.
"""
try:
# with contextlib.suppress(Exception):
response = self.client.get('/', timeout = timeout)
data = response.json()
# we should expect a 404 with a json response
# if self.debug_enabled: logger.info(f"API Ping: {data}\n{response.headers}")
if data.get('error'): return True
except Exception as e:
logger.error(f"API Ping Failed: {e}")
return False

async def aping(self, timeout: Optional[float] = 1.0) -> bool:
"""
Pings the API Endpoint to check if it's alive.
"""
with contextlib.suppress(Exception):
response = await self.client.async_get('/', timeout = timeout)
data = response.json()
# we should expect a 404 with a json response
if data.get('error'): return True
return False


class OpenAI(metaclass = OpenAIMetaClass):
"""
Interface for OpenAI
[V1] Interface for OpenAI
Deprecating this class in future versions
"""
pass

OpenAIManager: OpenAISessionManager = ProxyObject(OpenAISessionManager)



Loading

0 comments on commit 1bc9ab0

Please sign in to comment.