# How to Persist Model Selection and API Keys in the Database for TradingAgents-CN

> Learn how to persist model selection and API keys in the database for TradingAgents-CN. Discover MongoDB and Streamlit session state integration for robust configuration management.

- Repository: [hsliuping/TradingAgents-CN](https://github.com/hsliuping/tradingagents-cn)
- Tags: how-to-guide
- Published: 2026-02-16

---

**TradingAgents-CN persists UI model selections via Streamlit session state and URL parameters in [`web/utils/persistence.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/web/utils/persistence.py), while API keys and LLM configurations are stored in MongoDB through `ConfigService` in [`app/services/config_service.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/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`](https://github.com/hsliuping/TradingAgents-CN/blob/main/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.

```python

# 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.

```python

# 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`](https://github.com/hsliuping/TradingAgents-CN/blob/main/web/utils/persistence.py).

## Storing API Keys and LLM Configurations in MongoDB

Backend credentials are managed by `ConfigService` in [`app/services/config_service.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/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`](https://github.com/hsliuping/TradingAgents-CN/blob/main/app/models/config.py) defines the schema for storing provider credentials, including API keys.

```python

# 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`.

```python

# 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.

```python

# 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`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/config/config_manager.py) maps the provider to an environment variable and returns the value from `.env`.

```python

# 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:

```python

# ----------------------------------------------------------------------

# 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 `ModelPersistence` class in [`web/utils/persistence.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/web/utils/persistence.py) stores model selections in Streamlit's `st.session_state` and URL query parameters, ensuring choices survive page reloads and can be bookmarked.
- **Database Storage**: `ConfigService` in [`app/services/config_service.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/app/services/config_service.py) persists API keys and LLM configurations to the MongoDB `system_configs` collection using the `LLMConfig` Pydantic 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`](https://github.com/hsliuping/TradingAgents-CN/blob/main/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`](https://github.com/hsliuping/TradingAgents-CN/blob/main/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.