Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openai_utils.py - functionality for instantiating config_list with a .env file #68

Merged
merged 16 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions autogen/oai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
config_list_openai_aoai,
config_list_from_models,
config_list_from_json,
config_list_from_dotenv,
)

__all__ = [
Expand All @@ -15,4 +16,5 @@
"config_list_openai_aoai",
"config_list_from_models",
"config_list_from_json",
"config_list_from_dotenv",
]
140 changes: 140 additions & 0 deletions autogen/oai/openai_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import os
import json
import tempfile
from pathlib import Path
from typing import List, Optional, Dict, Set, Union
import logging
from dotenv import find_dotenv, load_dotenv


NON_CACHE_KEY = ["api_key", "api_base", "api_type", "api_version"]

Expand Down Expand Up @@ -239,3 +243,139 @@ def config_list_from_json(
except FileNotFoundError:
return []
return filter_config(config_list, filter_dict)


def get_config(
api_key: str, api_base: Optional[str] = None, api_type: Optional[str] = None, api_version: Optional[str] = None
) -> Dict:
"""
Construct a configuration dictionary with the provided API configurations.
Appending the additional configurations to the config only if they're set

example:
>> model_api_key_map={
"gpt-4": "OPENAI_API_KEY",
"gpt-3.5-turbo": {
"api_key_env_var": "ANOTHER_API_KEY",
"api_type": "aoai",
"api_version": "v2",
"api_base": "https://api.someotherapi.com"
}
}
Args:
api_key (str): The API key used for authenticating API requests.
api_base (str, optional): The base URL of the API. Defaults to None.
api_type (str, optional): The type or kind of API. Defaults to None.
api_version (str, optional): The API version. Defaults to None.

Returns:
Dict: A dictionary containing the API configurations.
"""
config = {"api_key": api_key}
if api_base:
config["api_base"] = api_base
if api_type:
config["api_type"] = api_type
if api_version:
config["api_version"] = api_version
return config


def config_list_from_dotenv(
dotenv_file_path: Optional[str] = None, model_api_key_map: Optional[dict] = None, filter_dict: Optional[dict] = None
) -> List[Dict[str, Union[str, Set[str]]]]:
"""
Load API configurations from a specified .env file or environment variables and construct a list of configurations.

This function will:
- Load API keys from a provided .env file or from existing environment variables.
- Create a configuration dictionary for each model using the API keys and additional configurations.
- Filter and return the configurations based on provided filters.

model_api_key_map will default to `{"gpt-4": "OPENAI_API_KEY", "gpt-3.5-turbo": "OPENAI_API_KEY"}` if none

Args:
dotenv_file_path (str, optional): The path to the .env file. Defaults to None.
model_api_key_map (str/dict, optional): A dictionary mapping models to their API key configurations.
If a string is provided as configuration, it is considered as an environment
variable name storing the API key.
If a dict is provided, it should contain at least 'api_key_env_var' key,
and optionally other API configurations like 'api_base', 'api_type', and 'api_version'.
Defaults to a basic map with 'gpt-4' and 'gpt-3.5-turbo' mapped to 'OPENAI_API_KEY'.
filter_dict (dict, optional): A dictionary containing the models to be loaded.
Containing a 'model' key mapped to a set of model names to be loaded.
Defaults to None, which loads all found configurations.

Returns:
List[Dict[str, Union[str, Set[str]]]]: A list of configuration dictionaries for each model.

Raises:
FileNotFoundError: If the specified .env file does not exist.
TypeError: If an unsupported type of configuration is provided in model_api_key_map.
"""
if dotenv_file_path:
dotenv_path = Path(dotenv_file_path)
if dotenv_path.exists():
load_dotenv(dotenv_path)
else:
logging.warning(f"The specified .env file {dotenv_path} does not exist.")
else:
dotenv_path = find_dotenv()
if not dotenv_path:
logging.warning("No .env file found. Loading configurations from environment variables.")
load_dotenv(dotenv_path)

# Ensure the model_api_key_map is not None to prevent TypeErrors during key assignment.
model_api_key_map = model_api_key_map or {}

# Ensure default models are always considered
default_models = ["gpt-4", "gpt-3.5-turbo"]

for model in default_models:
# Only assign default API key if the model is not present in the map.
# If model is present but set to invalid/empty, do not overwrite.
if model not in model_api_key_map:
model_api_key_map[model] = "OPENAI_API_KEY"

env_var = []
# Loop over the models and create configuration dictionaries
for model, config in model_api_key_map.items():
if isinstance(config, str):
api_key_env_var = config
config_dict = get_config(api_key=os.getenv(api_key_env_var))
elif isinstance(config, dict):
api_key = os.getenv(config.get("api_key_env_var", "OPENAI_API_KEY"))
config_without_key_var = {k: v for k, v in config.items() if k != "api_key_env_var"}
config_dict = get_config(api_key=api_key, **config_without_key_var)
else:
logging.warning(f"Unsupported type {type(config)} for model {model} configuration")

if not config_dict["api_key"] or config_dict["api_key"].strip() == "":
logging.warning(
f"API key not found or empty for model {model}. Please ensure path to .env file is correct."
)
continue # Skip this configuration and continue with the next

# Add model to the configuration and append to the list
config_dict["model"] = model
env_var.append(config_dict)

fd, temp_name = tempfile.mkstemp()
try:
with os.fdopen(fd, "w+") as temp:
env_var_str = json.dumps(env_var)
temp.write(env_var_str)
temp.flush()

# Assuming config_list_from_json is a valid function from your code
config_list = config_list_from_json(env_or_file=temp_name, filter_dict=filter_dict)
finally:
# The file is deleted after using its name (to prevent windows build from breaking)
os.remove(temp_name)

if len(config_list) == 0:
logging.error("No configurations loaded.")
return []

logging.info(f"Models available: {[config['model'] for config in config_list]}")
return config_list
Loading
Loading