diff options
Diffstat (limited to 'autogpt/core/resource/model_providers/openai.py')
-rw-r--r-- | autogpt/core/resource/model_providers/openai.py | 373 |
1 files changed, 0 insertions, 373 deletions
diff --git a/autogpt/core/resource/model_providers/openai.py b/autogpt/core/resource/model_providers/openai.py deleted file mode 100644 index 3707796a1..000000000 --- a/autogpt/core/resource/model_providers/openai.py +++ /dev/null @@ -1,373 +0,0 @@ -import enum -import functools -import logging -import math -import time -from typing import Callable, ParamSpec, TypeVar - -import openai -from openai.error import APIError, RateLimitError - -from autogpt.core.configuration import ( - Configurable, - SystemConfiguration, - UserConfigurable, -) -from autogpt.core.resource.model_providers.schema import ( - Embedding, - EmbeddingModelProvider, - EmbeddingModelProviderModelInfo, - EmbeddingModelProviderModelResponse, - LanguageModelFunction, - LanguageModelMessage, - LanguageModelProvider, - LanguageModelProviderModelInfo, - LanguageModelProviderModelResponse, - ModelProviderBudget, - ModelProviderCredentials, - ModelProviderName, - ModelProviderService, - ModelProviderSettings, - ModelProviderUsage, -) - -OpenAIEmbeddingParser = Callable[[Embedding], Embedding] -OpenAIChatParser = Callable[[str], dict] - - -class OpenAIModelName(str, enum.Enum): - ADA = "text-embedding-ada-002" - GPT3 = "gpt-3.5-turbo-0613" - GPT3_16K = "gpt-3.5-turbo-16k-0613" - GPT4 = "gpt-4-0613" - GPT4_32K = "gpt-4-32k-0613" - - -OPEN_AI_EMBEDDING_MODELS = { - OpenAIModelName.ADA: EmbeddingModelProviderModelInfo( - name=OpenAIModelName.ADA, - service=ModelProviderService.EMBEDDING, - provider_name=ModelProviderName.OPENAI, - prompt_token_cost=0.0004, - completion_token_cost=0.0, - max_tokens=8191, - embedding_dimensions=1536, - ), -} - - -OPEN_AI_LANGUAGE_MODELS = { - OpenAIModelName.GPT3: LanguageModelProviderModelInfo( - name=OpenAIModelName.GPT3, - service=ModelProviderService.LANGUAGE, - provider_name=ModelProviderName.OPENAI, - prompt_token_cost=0.0015, - completion_token_cost=0.002, - max_tokens=4096, - ), - OpenAIModelName.GPT3_16K: LanguageModelProviderModelInfo( - name=OpenAIModelName.GPT3, - service=ModelProviderService.LANGUAGE, - provider_name=ModelProviderName.OPENAI, - prompt_token_cost=0.003, - completion_token_cost=0.002, - max_tokens=16384, - ), - OpenAIModelName.GPT4: LanguageModelProviderModelInfo( - name=OpenAIModelName.GPT4, - service=ModelProviderService.LANGUAGE, - provider_name=ModelProviderName.OPENAI, - prompt_token_cost=0.03, - completion_token_cost=0.06, - max_tokens=8192, - ), - OpenAIModelName.GPT4_32K: LanguageModelProviderModelInfo( - name=OpenAIModelName.GPT4_32K, - service=ModelProviderService.LANGUAGE, - provider_name=ModelProviderName.OPENAI, - prompt_token_cost=0.06, - completion_token_cost=0.12, - max_tokens=32768, - ), -} - - -OPEN_AI_MODELS = { - **OPEN_AI_LANGUAGE_MODELS, - **OPEN_AI_EMBEDDING_MODELS, -} - - -class OpenAIConfiguration(SystemConfiguration): - retries_per_request: int = UserConfigurable() - - -class OpenAIModelProviderBudget(ModelProviderBudget): - graceful_shutdown_threshold: float = UserConfigurable() - warning_threshold: float = UserConfigurable() - - -class OpenAISettings(ModelProviderSettings): - configuration: OpenAIConfiguration - credentials: ModelProviderCredentials() - budget: OpenAIModelProviderBudget - - -class OpenAIProvider( - Configurable, - LanguageModelProvider, - EmbeddingModelProvider, -): - default_settings = OpenAISettings( - name="openai_provider", - description="Provides access to OpenAI's API.", - configuration=OpenAIConfiguration( - retries_per_request=10, - ), - credentials=ModelProviderCredentials(), - budget=OpenAIModelProviderBudget( - total_budget=math.inf, - total_cost=0.0, - remaining_budget=math.inf, - usage=ModelProviderUsage( - prompt_tokens=0, - completion_tokens=0, - total_tokens=0, - ), - graceful_shutdown_threshold=0.005, - warning_threshold=0.01, - ), - ) - - def __init__( - self, - settings: OpenAISettings, - logger: logging.Logger, - ): - self._configuration = settings.configuration - self._credentials = settings.credentials - self._budget = settings.budget - - self._logger = logger - - retry_handler = _OpenAIRetryHandler( - logger=self._logger, - num_retries=self._configuration.retries_per_request, - ) - - self._create_completion = retry_handler(_create_completion) - self._create_embedding = retry_handler(_create_embedding) - - def get_token_limit(self, model_name: str) -> int: - """Get the token limit for a given model.""" - return OPEN_AI_MODELS[model_name].max_tokens - - def get_remaining_budget(self) -> float: - """Get the remaining budget.""" - return self._budget.remaining_budget - - async def create_language_completion( - self, - model_prompt: list[LanguageModelMessage], - functions: list[LanguageModelFunction], - model_name: OpenAIModelName, - completion_parser: Callable[[dict], dict], - **kwargs, - ) -> LanguageModelProviderModelResponse: - """Create a completion using the OpenAI API.""" - completion_kwargs = self._get_completion_kwargs(model_name, functions, **kwargs) - response = await self._create_completion( - messages=model_prompt, - **completion_kwargs, - ) - response_args = { - "model_info": OPEN_AI_LANGUAGE_MODELS[model_name], - "prompt_tokens_used": response.usage.prompt_tokens, - "completion_tokens_used": response.usage.completion_tokens, - } - - parsed_response = completion_parser( - response.choices[0].message.to_dict_recursive() - ) - response = LanguageModelProviderModelResponse( - content=parsed_response, **response_args - ) - self._budget.update_usage_and_cost(response) - return response - - async def create_embedding( - self, - text: str, - model_name: OpenAIModelName, - embedding_parser: Callable[[Embedding], Embedding], - **kwargs, - ) -> EmbeddingModelProviderModelResponse: - """Create an embedding using the OpenAI API.""" - embedding_kwargs = self._get_embedding_kwargs(model_name, **kwargs) - response = await self._create_embedding(text=text, **embedding_kwargs) - - response_args = { - "model_info": OPEN_AI_EMBEDDING_MODELS[model_name], - "prompt_tokens_used": response.usage.prompt_tokens, - "completion_tokens_used": response.usage.completion_tokens, - } - response = EmbeddingModelProviderModelResponse( - **response_args, - embedding=embedding_parser(response.embeddings[0]), - ) - self._budget.update_usage_and_cost(response) - return response - - def _get_completion_kwargs( - self, - model_name: OpenAIModelName, - functions: list[LanguageModelFunction], - **kwargs, - ) -> dict: - """Get kwargs for completion API call. - - Args: - model: The model to use. - kwargs: Keyword arguments to override the default values. - - Returns: - The kwargs for the chat API call. - - """ - completion_kwargs = { - "model": model_name, - **kwargs, - **self._credentials.unmasked(), - } - if functions: - completion_kwargs["functions"] = functions - - return completion_kwargs - - def _get_embedding_kwargs( - self, - model_name: OpenAIModelName, - **kwargs, - ) -> dict: - """Get kwargs for embedding API call. - - Args: - model: The model to use. - kwargs: Keyword arguments to override the default values. - - Returns: - The kwargs for the embedding API call. - - """ - embedding_kwargs = { - "model": model_name, - **kwargs, - **self._credentials.unmasked(), - } - - return embedding_kwargs - - def __repr__(self): - return "OpenAIProvider()" - - -async def _create_embedding(text: str, *_, **kwargs) -> openai.Embedding: - """Embed text using the OpenAI API. - - Args: - text str: The text to embed. - model_name str: The name of the model to use. - - Returns: - str: The embedding. - """ - return await openai.Embedding.acreate( - input=[text], - **kwargs, - ) - - -async def _create_completion( - messages: list[LanguageModelMessage], *_, **kwargs -) -> openai.Completion: - """Create a chat completion using the OpenAI API. - - Args: - messages: The prompt to use. - - Returns: - The completion. - - """ - messages = [message.dict() for message in messages] - if "functions" in kwargs: - kwargs["functions"] = [function.json_schema for function in kwargs["functions"]] - return await openai.ChatCompletion.acreate( - messages=messages, - **kwargs, - ) - - -_T = TypeVar("_T") -_P = ParamSpec("_P") - - -class _OpenAIRetryHandler: - """Retry Handler for OpenAI API call. - - Args: - num_retries int: Number of retries. Defaults to 10. - backoff_base float: Base for exponential backoff. Defaults to 2. - warn_user bool: Whether to warn the user. Defaults to True. - """ - - _retry_limit_msg = "Error: Reached rate limit, passing..." - _api_key_error_msg = ( - "Please double check that you have setup a PAID OpenAI API Account. You can " - "read more here: https://docs.agpt.co/setup/#getting-an-api-key" - ) - _backoff_msg = "Error: API Bad gateway. Waiting {backoff} seconds..." - - def __init__( - self, - logger: logging.Logger, - num_retries: int = 10, - backoff_base: float = 2.0, - warn_user: bool = True, - ): - self._logger = logger - self._num_retries = num_retries - self._backoff_base = backoff_base - self._warn_user = warn_user - - def _log_rate_limit_error(self) -> None: - self._logger.debug(self._retry_limit_msg) - if self._warn_user: - self._logger.warning(self._api_key_error_msg) - self._warn_user = False - - def _backoff(self, attempt: int) -> None: - backoff = self._backoff_base ** (attempt + 2) - self._logger.debug(self._backoff_msg.format(backoff=backoff)) - time.sleep(backoff) - - def __call__(self, func: Callable[_P, _T]) -> Callable[_P, _T]: - @functools.wraps(func) - async def _wrapped(*args: _P.args, **kwargs: _P.kwargs) -> _T: - num_attempts = self._num_retries + 1 # +1 for the first attempt - for attempt in range(1, num_attempts + 1): - try: - return await func(*args, **kwargs) - - except RateLimitError: - if attempt == num_attempts: - raise - self._log_rate_limit_error() - - except APIError as e: - if (e.http_status != 502) or (attempt == num_attempts): - raise - - self._backoff(attempt) - - return _wrapped |