fix(agent, forge): Make LLM API key check provider-agnostic (#7220)

* Rename `assert_config_has_openai_api_key` to `assert_config_has_required_llm_api_keys`
* Make OpenAI credential check conditional (only if an OpenAI model is selected in the config)
* Implement checks for Groq and Anthropic credentials
* Use API calls for Groq and OpenAI credential checks to make sure the keys are valid
pull/7224/head
Reinier van der Leer 2024-06-14 20:56:03 +02:00 committed by GitHub
parent 66f373fb57
commit 227cf41612
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 93 additions and 55 deletions

View File

@ -21,7 +21,11 @@ from forge.components.code_executor.code_executor import (
)
from forge.config.ai_directives import AIDirectives
from forge.config.ai_profile import AIProfile
from forge.config.config import Config, ConfigBuilder, assert_config_has_openai_api_key
from forge.config.config import (
Config,
ConfigBuilder,
assert_config_has_required_llm_api_keys,
)
from forge.file_storage import FileStorageBackendName, get_storage
from forge.llm.providers import MultiProvider
from forge.logging.config import configure_logging
@ -98,8 +102,7 @@ async def run_auto_gpt(
tts_config=config.tts_config,
)
# TODO: fill in llm values here
assert_config_has_openai_api_key(config)
await assert_config_has_required_llm_api_keys(config)
await apply_overrides_to_config(
config=config,
@ -380,8 +383,7 @@ async def run_auto_gpt_server(
tts_config=config.tts_config,
)
# TODO: fill in llm values here
assert_config_has_openai_api_key(config)
await assert_config_has_required_llm_api_keys(config)
await apply_overrides_to_config(
config=config,

View File

@ -3,10 +3,10 @@ This module contains configuration models and helpers for AutoGPT Forge.
"""
from .ai_directives import AIDirectives
from .ai_profile import AIProfile
from .config import Config, ConfigBuilder, assert_config_has_openai_api_key
from .config import Config, ConfigBuilder, assert_config_has_required_llm_api_keys
__all__ = [
"assert_config_has_openai_api_key",
"assert_config_has_required_llm_api_keys",
"AIProfile",
"AIDirectives",
"Config",

View File

@ -7,8 +7,6 @@ import re
from pathlib import Path
from typing import Any, Optional, Union
import click
from colorama import Fore
from pydantic import SecretStr, validator
import forge
@ -208,55 +206,93 @@ class ConfigBuilder(Configurable[Config]):
return config
def assert_config_has_openai_api_key(config: Config) -> None:
"""Check if the OpenAI API key is set in config.py or as an environment variable."""
key_pattern = r"^sk-(proj-)?\w{48}"
openai_api_key = (
config.openai_credentials.api_key.get_secret_value()
if config.openai_credentials
else ""
)
async def assert_config_has_required_llm_api_keys(config: Config) -> None:
"""
Check if API keys (if required) are set for the configured SMART_LLM and FAST_LLM.
"""
from pydantic import ValidationError
# If there's no credentials or empty API key, prompt the user to set it
if not openai_api_key:
logger.error(
"Please set your OpenAI API key in .env or as an environment variable."
)
logger.info(
"You can get your key from https://platform.openai.com/account/api-keys"
)
openai_api_key = click.prompt(
"Please enter your OpenAI API key if you have it",
default="",
show_default=False,
)
openai_api_key = openai_api_key.strip()
if re.search(key_pattern, openai_api_key):
os.environ["OPENAI_API_KEY"] = openai_api_key
if config.openai_credentials:
config.openai_credentials.api_key = SecretStr(openai_api_key)
else:
config.openai_credentials = OpenAICredentials(
api_key=SecretStr(openai_api_key)
from forge.llm.providers.anthropic import AnthropicModelName
from forge.llm.providers.groq import GroqModelName
if set((config.smart_llm, config.fast_llm)).intersection(AnthropicModelName):
from forge.llm.providers.anthropic import AnthropicCredentials
try:
credentials = AnthropicCredentials.from_env()
except ValidationError as e:
if "api_key" in str(e):
logger.error(
"Set your Anthropic API key in .env or as an environment variable"
)
print("OpenAI API key successfully set!")
print(
f"{Fore.YELLOW}NOTE: The API key you've set is only temporary. "
f"For longer sessions, please set it in the .env file{Fore.RESET}"
logger.info(
"For further instructions: "
"https://docs.agpt.co/autogpt/setup/#anthropic"
)
raise ValueError("Anthropic is unavailable: can't load credentials") from e
key_pattern = r"^sk-ant-api03-[\w\-]{95}"
# If key is set, but it looks invalid
if not re.search(key_pattern, credentials.api_key.get_secret_value()):
logger.warning(
"Possibly invalid Anthropic API key! "
f"Configured Anthropic API key does not match pattern '{key_pattern}'. "
"If this is a valid key, please report this warning to the maintainers."
)
else:
print(f"{Fore.RED}Invalid OpenAI API key{Fore.RESET}")
exit(1)
# If key is set, but it looks invalid
elif not re.search(key_pattern, openai_api_key):
logger.error(
"Invalid OpenAI API key! "
"Please set your OpenAI API key in .env or as an environment variable."
)
logger.info(
"You can get your key from https://platform.openai.com/account/api-keys"
)
exit(1)
if set((config.smart_llm, config.fast_llm)).intersection(GroqModelName):
from groq import AuthenticationError
from forge.llm.providers.groq import GroqProvider
try:
groq = GroqProvider()
await groq.get_available_models()
except ValidationError as e:
if "api_key" not in str(e):
raise
logger.error("Set your Groq API key in .env or as an environment variable")
logger.info(
"For further instructions: https://docs.agpt.co/autogpt/setup/#groq"
)
raise ValueError("Groq is unavailable: can't load credentials")
except AuthenticationError as e:
logger.error("The Groq API key is invalid!")
logger.info(
"For instructions to get and set a new API key: "
"https://docs.agpt.co/autogpt/setup/#groq"
)
raise ValueError("Groq is unavailable: invalid API key") from e
if set((config.smart_llm, config.fast_llm)).intersection(OpenAIModelName):
from openai import AuthenticationError
from forge.llm.providers.openai import OpenAIProvider
try:
openai = OpenAIProvider()
await openai.get_available_models()
except ValidationError as e:
if "api_key" not in str(e):
raise
logger.error(
"Set your OpenAI API key in .env or as an environment variable"
)
logger.info(
"For further instructions: https://docs.agpt.co/autogpt/setup/#openai"
)
raise ValueError("OpenAI is unavailable: can't load credentials")
except AuthenticationError as e:
logger.error("The OpenAI API key is invalid!")
logger.info(
"For instructions to get and set a new API key: "
"https://docs.agpt.co/autogpt/setup/#openai"
)
raise ValueError("OpenAI is unavailable: invalid API key") from e
def _safe_split(s: Union[str, None], sep: str = ",") -> list[str]: