aisuite Provider Authentication Pattern: API Keys, IAM Roles, and Environment Variables Explained

aisuite implements provider-specific authentication through a base Provider class with a standardized **config constructor that falls back to environment variables, enabling both explicit API keys and implicit IAM role-based access.

aisuite unifies dozens of LLM and ASR services behind a single interface. The library's authentication architecture lets developers pass credentials directly or rely on environment-based discovery, depending on each provider's security model. This article examines the implementation pattern for provider-specific authentication in aisuite, from the abstract base class through concrete provider implementations.

The Three-Layer Authentication Architecture

aisuite separates authentication concerns across three components:

  • Provider abstract base class — defines the contract all providers must implement
  • ProviderFactory — dynamically loads provider modules using naming conventions
  • Concrete provider classes — implement credential retrieval and client initialization

Base Provider Contract

The abstract base class in aisuite/provider.py establishes the interface that every provider must satisfy.


# aisuite/provider.py#L24-L30

class Provider(ABC):
    @abstractmethod
    def chat_completions_create(self, model, messages, **kwargs):
        """Create a chat completion for the given model."""
        pass

Concrete providers inherit from Provider and only implement the chat and audio methods. Authentication logic lives entirely in the subclass constructor.

ProviderFactory: Dynamic Loading with Config Injection

The ProviderFactory.create_provider() method handles runtime discovery and instantiation. It imports modules following the pattern <provider_key>_provider and instantiates <ProviderKey>Provider, passing the complete configuration dictionary.


# aisuite/provider.py#L92-L106

@staticmethod
def create_provider(provider_key: str, config: dict):
    module_name = f"aisuite.providers.{provider_key}_provider"
    module = importlib.import_module(module_name)
    
    class_name = f"{provider_key.title().replace('_', '')}Provider"
    provider_class = getattr(module, class_name)
    
    return provider_class(**config)

This design means all authentication parameters travel through the **config dictionary, with each provider deciding how to interpret those values.

The Six-Step Authentication Pattern

Every concrete provider in aisuite follows an identical credential handling sequence:

  1. Accept **config — constructor signature is always def __init__(self, **config)
  2. Prefer explicit config — check config.get("api_key"), config.get("base_url"), etc.
  3. Fall back to environment — use os.getenv("PROVIDER_API_KEY") when config values are absent
  4. Validate presence — raise ValueError with a clear message if required credentials are missing
  5. Initialize vendor client — pass normalized config to the underlying SDK
  6. Store client and converter — keep references for request/response normalization

API Key-Based Authentication Examples

OpenAI Provider

OpenAI requires an API key that can come from either explicit config or the OPENAI_API_KEY environment variable.


# aisuite/providers/openai_provider.py#L15-L23

class OpenAiProvider(Provider):
    def __init__(self, **config):
        self.config = config
        # config dict is passed directly to openai.OpenAI()

        # which reads api_key from config or OPENAI_API_KEY env var

        self.client = openai.OpenAI(**config)

The openai Python SDK itself handles the environment fallback, so aisuite delegates this responsibility.

Azure Provider

Azure OpenAI requires both an API key and base URL, with explicit validation.


# aisuite/providers/azure_provider.py#L81-L86

def __init__(self, **config):
    self.api_key = config.get("api_key", os.getenv("AZURE_API_KEY"))
    self.base_url = config.get("base_url", os.getenv("AZURE_BASE_URL"))
    
    if not self.api_key or not self.base_url:
        raise ValueError("Azure API key and base URL are required")

Groq Provider

Groq demonstrates the full pattern with explicit environment fallback.


# aisuite/providers/groq_provider.py#L31-L40

class GroqProvider(Provider):
    def __init__(self, **config):
        self.api_key = config.get("api_key", os.getenv("GROQ_API_KEY"))
        
        if not self.api_key:
            raise ValueError("GROQ_API_KEY is not set")
        
        self.client = groq.Groq(api_key=self.api_key)

IAM Role Authentication: AWS Bedrock Pattern

AWS Bedrock represents the alternative model where no API key exists. Instead, authentication relies on AWS's standard credential provider chain.


# aisuite/providers/aws_provider.py#L17-L23

class AwsProvider(Provider):
    def __init__(self, **config):
        self.config = BedrockConfig(**config)  # reads AWS_REGION env var, defaults to us-west-2

        self.client = self.config.create_client()  # boto3 resolves IAM role, instance profile, etc.

The BedrockConfig class extracts the region from config or AWS_REGION, then boto3.client("bedrock-runtime") automatically discovers credentials through:

  • Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  • IAM roles attached to EC2 instances or ECS tasks
  • AWS SSO or IAM Identity Center profiles
  • Local AWS CLI configuration

This means zero secrets appear in code or environment variables when running on properly configured AWS infrastructure.

Practical Usage Examples

Explicit API Key Configuration

from aisuite.provider import ProviderFactory

config = {
    "api_key": "sk-my-openai-key",  # omit if OPENAI_API_KEY is set

    "base_url": "https://api.openai.com/v1"
}
openai = ProviderFactory.create_provider("openai", config)

response = openai.chat_completions_create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello"}]
)
print(response)

IAM Role-Based Access (No Secrets Required)


# Running on AWS infrastructure with IAM role attached

aws = ProviderFactory.create_provider("aws", {})  # empty config

resp = aws.chat_completions_create(
    model="anthropic.claude-v2",
    messages=[{"role": "user", "content": "Summarize this text"}],
    maxTokens=512,
    temperature=0.7
)
print(resp)

Direct Provider Instantiation with Environment Fallback

import os
os.environ["GROQ_API_KEY"] = "gsk-my-groq-key"

from aisuite.providers.groq_provider import GroqProvider

groq = GroqProvider()  # api_key pulled from GROQ_API_KEY automatically

stream = groq.chat_completions_create(
    model="llama3-groq-70b-8192-tool-use-preview",
    messages=[{"role": "user", "content": "What is the capital of France?"}]
)
print(stream)

Key Implementation Files

File Purpose
aisuite/provider.py Abstract Provider class and ProviderFactory
aisuite/providers/openai_provider.py OpenAI API key handling
aisuite/providers/azure_provider.py Azure OpenAI with dual credential requirement
aisuite/providers/groq_provider.py Groq with environment fallback
aisuite/providers/aws_provider.py AWS Bedrock IAM role authentication
aisuite/providers/message_converter.py Request/response normalization

Summary

  • aisuite authentication is provider-agnostic at the interface level — the ProviderFactory and base class know nothing about credential mechanisms
  • Concrete providers implement the six-step pattern: accept **config, prefer explicit values, fall back to environment, validate, initialize client, store references
  • API key providers (OpenAI, Groq, Azure) require explicit secrets that can pass through config or environment variables
  • IAM role providers (AWS Bedrock) delegate credential discovery to the underlying SDK, enabling secret-free operation on cloud infrastructure
  • Adding new providers requires only: creating <name>_provider.py, inheriting from Provider, and following the constructor convention

Frequently Asked Questions

How does aisuite handle missing API keys?

Each concrete provider validates required credentials in __init__ and raises a descriptive ValueError if credentials are absent. For example, the Azure provider checks both api_key and base_url, while Groq validates only api_key.

Can I use AWS Bedrock without any environment variables?

Only AWS_REGION is recommended; it defaults to us-west-2 if unset. No API keys, access keys, or secrets are required when running on AWS infrastructure with an attached IAM role. The boto3 library resolves credentials through the standard AWS credential provider chain.

Why does aisuite use **config instead of explicit parameters?

The **config pattern enables forward compatibility and provider-specific extensions without modifying the base interface. Each provider extracts the keys it needs while ignoring others, allowing the same configuration dictionary to contain parameters for multiple providers simultaneously.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →