How to Persist Model Selection and API Keys in the Database for TradingAgents-CN
TradingAgents-CN persists UI model selections via Streamlit session state and URL parameters in web/utils/persistence.py, while API keys and LLM configurations are stored in MongoDB through ConfigService in app/services/config_service.py.
TradingAgents-CN is an open-source trading agent framework that separates user interface preferences from sensitive backend credentials. To persist model selection and API keys in the database effectively, the system implements a dual-layer persistence strategy that combines Streamlit's session management for UI state with MongoDB storage for secure configuration data.
Understanding the Dual Persistence Architecture
TradingAgents-CN handles two distinct types of persistence. The UI selection (provider, category, model) must survive page reloads and support bookmarkable URLs, while API credentials require secure storage in a database with environment variable fallback.
Persisting UI Model Selections with ModelPersistence
The web/utils/persistence.py module provides the ModelPersistence class, which synchronizes the user's model choice between Streamlit's st.session_state and the browser URL query parameters.
Loading Model Selections from Session and URL
When a page renders, load_model_selection() checks st.query_params first, then falls back to st.session_state, and finally to defaults.
# web/utils/persistence.py
def load_model_selection():
"""加载模型选择"""
return persistence.load_config()
Saving Model Selections to Session and URL
When the user confirms a selection, save_model_selection(provider, category, model) updates both the session state and the URL query string, enabling bookmarkable configurations.
# web/utils/persistence.py
def save_model_selection(provider, category="", model=""):
"""保存模型选择"""
persistence.save_config(provider, category, model)
Clearing Stored Selections
The clear_model_selection() method removes the entry from session state and clears URL parameters, resetting the UI to defaults. The implementation resides in the ModelPersistence class at lines 13-30 of web/utils/persistence.py.
Storing API Keys and LLM Configurations in MongoDB
Backend credentials are managed by ConfigService in app/services/config_service.py, which persists LLMConfig objects to the system_configs collection in MongoDB.
The LLMConfig Data Model
The LLMConfig Pydantic model in app/models/config.py defines the schema for storing provider credentials, including API keys.
# app/models/config.py
class LLMConfig(BaseModel):
provider: ModelProvider
model_name: str
api_key: str = ""
api_base: Optional[str] = None
max_tokens: int = 4000
temperature: float = 0.7
enabled: bool = False
Writing Configurations with ConfigService
The update_llm_config() method in ConfigService upserts the configuration into MongoDB and synchronizes with the legacy JSON configuration via unified_config.
# app/services/config_service.py
async def update_llm_config(self, llm_config: LLMConfig) -> bool:
"""更新大模型配置"""
# 1️⃣ 更新统一配置(文件层)
success = unified_config.save_llm_config(llm_config)
if not success:
return False
# 2️⃣ 更新 MongoDB 中的系统配置
config = await self.get_system_config()
for i, existing in enumerate(config.llm_configs):
if existing.model_name == llm_config.model_name:
config.llm_configs[i] = llm_config
break
else:
config.llm_configs.append(llm_config)
return await self.save_system_config(config)
Reading Configurations from the Database
get_system_config() retrieves the active SystemConfig document from the system_configs collection, returning a list of LLMConfig objects with stored API keys.
# app/services/config_service.py
async def get_system_config(self) -> Optional[SystemConfig]:
"""获取系统配置 - 优先从数据库获取最新数据"""
db = await self._get_db()
config_collection = db.system_configs
cfg = await config_collection.find_one({"is_active": True}, sort=[("version", -1)])
return SystemConfig(**cfg) if cfg else await self._create_default_config()
Environment Variable Fallback
When no API key exists in the database, ConfigManager._get_env_api_key() in tradingagents/config/config_manager.py maps the provider to an environment variable and returns the value from .env.
# tradingagents/config/config_manager.py
def _get_env_api_key(self, provider: str) -> str:
env_key_map = {
"dashscope": "DASHSCOPE_API_KEY",
"openai": "OPENAI_API_KEY",
"google": "GOOGLE_API_KEY",
"anthropic":"ANTHROPIC_API_KEY",
"deepseek": "DEEPSEEK_API_KEY",
}
env_key = env_key_map.get(provider.lower())
return os.getenv(env_key, "") if env_key else ""
Complete Workflow Example
The following example demonstrates the complete flow from UI selection to database persistence:
# ----------------------------------------------------------------------
# 1️⃣ UI → user picks a model
# ----------------------------------------------------------------------
from web.utils.persistence import save_model_selection, load_model_selection
# When the page loads
selected = load_model_selection() # {'provider': ..., 'category': ..., 'model': ...}
st.selectbox("Provider", options, index=…) # UI widget
if st.button("Save"):
save_model_selection(provider, category, model)
# ----------------------------------------------------------------------
# 2️⃣ Back‑end → store the credential in MongoDB
# ----------------------------------------------------------------------
from app.services.config_service import ConfigService
from app.models.config import LLMConfig, ModelProvider
async def persist_api_key():
cfg = ConfigService() # optional DB manager can be passed
llm_cfg = LLMConfig(
provider=ModelProvider.DASHSCOPE,
model_name="qwen-turbo",
api_key="sk‑your‑dashscope‑key",
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
enabled=True,
)
await cfg.update_llm_config(llm_cfg) # writes to `system_configs` collection
# ----------------------------------------------------------------------
# 3️⃣ Later request → retrieve the stored key
# ----------------------------------------------------------------------
async def get_key_for_request():
cfg = ConfigService()
system = await cfg.get_system_config()
# Find the config for the selected model
for llm in system.llm_configs:
if llm.provider == ModelProvider.DASHSCOPE and llm.model_name == "qwen-turbo":
return llm.api_key # ← the persisted key
# fallback to .env if not found
return cfg._get_env_api_key("dashscope")
Summary
- UI State Persistence: The
ModelPersistenceclass inweb/utils/persistence.pystores model selections in Streamlit'sst.session_stateand URL query parameters, ensuring choices survive page reloads and can be bookmarked. - Database Storage:
ConfigServiceinapp/services/config_service.pypersists API keys and LLM configurations to the MongoDBsystem_configscollection using theLLMConfigPydantic model. - Dual Write Strategy: The
update_llm_config()method synchronizes data between the JSON configuration file and MongoDB, ensuring backward compatibility. - Environment Fallback: When database records lack API keys,
ConfigManager._get_env_api_key()retrieves values from environment variables defined in.env.
Frequently Asked Questions
Where does TradingAgents-CN store the user's selected model?
TradingAgents-CN stores the user's selected model in two places for redundancy: Streamlit's st.session_state and the browser's URL query parameters. The ModelPersistence class in web/utils/persistence.py manages this through load_model_selection() and save_model_selection(), allowing the selection to persist across page reloads and enabling bookmarkable configurations.
How are API keys encrypted in the database?
The raw source code indicates that API keys are stored as plain text strings within the LLMConfig Pydantic model in the MongoDB system_configs collection. While the ConfigService handles the persistence layer, the provided implementation does not show explicit encryption at the application level. For production deployments, you should implement field-level encryption or use MongoDB's Client-Side Field Level Encryption (CSFLE) to protect these credentials.
What happens if the MongoDB connection fails?
When MongoDB is unavailable, ConfigService falls back to the legacy JSON configuration file through the unified_config helper. The get_system_config() method attempts to read from the database first, but if that fails or returns no data, it creates or reads from a default local configuration. This ensures the application remains functional even when the database connection is interrupted, though changes made during an outage may not persist to MongoDB until connectivity is restored.
Can I use environment variables instead of database storage?
Yes, TradingAgents-CN supports environment variables as a fallback mechanism. The ConfigManager class in tradingagents/config/config_manager.py provides the _get_env_api_key() method, which maps provider names to specific environment variables such as DASHSCOPE_API_KEY, OPENAI_API_KEY, and ANTHROPIC_API_KEY. If ConfigService retrieves a configuration with an empty API key from MongoDB, the system automatically falls back to these environment variables defined in your .env file.
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 →