How to Configure and Manage API Keys for Different LLM Providers in AI Hedge Fund
You can configure API keys for OpenAI, Groq, Anthropic, and other LLM providers in the virattt/ai-hedge-fund repository by setting environment variables in a .env file or passing per-request overrides through the request state metadata.
The AI Hedge Fund project integrates with multiple Large Language Model (LLM) providers and financial data services, requiring a flexible and secure credential management system. According to the source code in virattt/ai-hedge-fund, all secrets are injected at runtime via environment variables or request-level metadata, ensuring no hardcoded keys exist in the codebase. This guide explains how to configure and manage API keys for different providers while maintaining security best practices.
Where API Keys Are Stored
Environment Variables and .env.example
The repository uses a template-based approach for initial configuration. The .env.example file ships with placeholder values for every supported provider, including OPENAI_API_KEY, GROQ_API_KEY, ANTHROPIC_API_KEY, DEEPSEEK_API_KEY, GOOGLE_API_KEY, OPENROUTER_API_KEY, and FINANCIAL_DATASETS_API_KEY.
Users copy this file to .env and populate it with real secrets. The python-dotenv loader injects these into os.getenv, making them available throughout the application. This approach keeps credentials out of version control while providing a clear specification of required variables.
Request-Level Overrides
For multi-tenant deployments or user-specific key management, the system supports per-request API keys. Clients can attach an api_keys dictionary to the request metadata, allowing individual users to supply their own credentials. These overrides take precedence over environment variables and are extracted via the helper function in src/utils/api_key.py.
How the Code Retrieves API Keys
The retrieval logic follows a strict hierarchy in two primary locations:
src/utils/api_key.py– Handles financial data keys and generic extraction from statesrc/llm/models.py– Handles LLM provider keys via theget_modelfactory function
The get_api_key_from_state function (lines 3-8 in src/utils/api_key.py) checks for request-level overrides:
def get_api_key_from_state(state: dict, api_key_name: str) -> str:
if state and state.get("metadata", {}).get("request"):
request = state["metadata"]["request"]
if hasattr(request, 'api_keys') and request.api_keys:
return request.api_keys.get(api_key_name)
return None
For LLM providers, the get_model function in src/llm/models.py implements a fallback pattern. It first checks an explicit api_keys dictionary passed from the request, then falls back to os.getenv. If neither source provides the key, it raises a clear ValueError.
Configuring LLM Provider Keys
Each supported provider follows an identical configuration pattern in src/llm/models.py. The system checks for the provider-specific environment variable only after exhausting request-level overrides.
The OpenAI implementation (lines 38-45) demonstrates this pattern:
# src/llm/models.py – OpenAI example
api_key = (api_keys or {}).get("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError("OpenAI API key not found …")
This pattern repeats for every provider with their respective variable names:
- Groq:
GROQ_API_KEY - Anthropic:
ANTHROPIC_API_KEY - DeepSeek:
DEEPSEEK_API_KEY - Google:
GOOGLE_API_KEY - OpenRouter:
OPENROUTER_API_KEY
The call_llm function in src/utils/llm.py (lines 42-49) orchestrates this by gathering the model configuration from the agent, then invoking get_model with the appropriate provider and key dictionary extracted from state.
Configuring Financial Data API Keys
Financial data access uses the same dual-source approach but relies on the get_api_key_from_state helper. Agents requiring market data—such as the Warren Buffett agent—call this utility to retrieve the FINANCIAL_DATASETS_API_KEY.
In src/agents/warren_buffett.py (line 24), the implementation looks like this:
api_key = get_api_key_from_state(state, "FINANCIAL_DATASETS_API_KEY")
If the request metadata contains this key, the function returns it immediately. Otherwise, the agent falls back to the global environment variable defined in .env. Other agents like valuation.py and technicals.py follow this identical pattern.
Per-Request API Key Override Workflow
To override keys for a specific request, populate the state object with a metadata dictionary containing the request object and its api_keys attribute:
# Create request with provider-specific overrides
request = {
"api_keys": {
"OPENAI_API_KEY": "sk-user-provided-key",
"GROQ_API_KEY": "gsk-user-provided-key",
"FINANCIAL_DATASETS_API_KEY": "user-fd-key",
}
}
# Initialize state structure
state = {
"metadata": {"request": request},
# ... other shared state ...
}
# The agent will now use request-level keys
from src.utils.api_key import get_api_key_from_state
fd_key = get_api_key_from_state(state, "FINANCIAL_DATASETS_API_KEY")
When call_llm executes, it passes this state through the pipeline. The get_model function receives the api_keys dictionary from the request state and uses it in preference to environment variables, enabling secure multi-user deployments where each request carries its own credentials.
Security Best Practices
The repository enforces a strict secrets-out-of-code policy. Keys are never logged, and missing credentials trigger explicit exceptions rather than silent failures. When running in Docker or CI environments, inject secrets via runtime environment variables rather than baking them into images. For production deployments, consider using a secrets manager that populates environment variables at startup, maintaining the same interface while adding rotation capabilities.
Summary
- Configuration Template: Copy
.env.exampleto.envand fill in provider-specific keys likeOPENAI_API_KEYandGROQ_API_KEY. - Runtime Retrieval: The system uses
os.getenvinsrc/llm/models.pyandget_api_key_from_stateinsrc/utils/api_key.pyto resolve credentials. - Request Overrides: Pass custom keys via
state["metadata"]["request"].api_keysto override environment variables for specific requests. - Error Handling: Missing keys raise
ValueErrorimmediately, preventing silent authentication failures. - Financial Data: Agents use the same state-based helper to access
FINANCIAL_DATASETS_API_KEYwithout hardcoding references.
Frequently Asked Questions
What happens if an API key is missing at runtime?
The application raises a ValueError with a descriptive message indicating which provider key is missing. In src/llm/models.py, each provider check explicitly validates the key presence after checking both the request dictionary and environment variables, ensuring you know immediately which credential to supply rather than receiving a cryptic authentication error from the provider.
Can I use different API keys for different agents in the same request?
Yes. Since the api_keys dictionary in the request state can contain multiple provider keys (e.g., both OPENAI_API_KEY and GROQ_API_KEY), different agents can call different providers within the same workflow. Each agent's call_llm invocation extracts the specific key required for its configured model provider from the shared state, allowing heterogeneous agent composition using distinct credentials.
How do I add support for a new LLM provider in the configuration system?
Add the provider-specific environment variable name (e.g., NEW_PROVIDER_API_KEY) to .env.example, then implement the key retrieval logic in src/llm/models.py following the existing pattern: check (api_keys or {}).get("NEW_PROVIDER_API_KEY") first, then fall back to os.getenv("NEW_PROVIDER_API_KEY"), and raise ValueError if neither exists. Finally, instantiate the appropriate LangChain model class with the resolved key.
Is it safe to commit the .env file to version control?
No. The .env file contains sensitive secrets and should be listed in .gitignore. Only commit .env.example, which contains placeholder values. The example file serves as documentation for required variables without exposing actual credentials, maintaining the repository's security posture while helping new developers understand which API keys they need to obtain.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →