# How to Add a New LLM Adapter for a Custom Provider in TradingAgents-CN

> Learn to add a new LLM adapter for a custom provider in TradingAgents-CN. This guide covers Python module creation, LangChain subclassing, and export for seamless integration.

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

---

**To add a new LLM adapter for a custom provider in TradingAgents-CN, create a Python module in `tradingagents/llm_adapters/`, subclass a LangChain base class such as `ChatOpenAI`, implement `__init__` for API key resolution, override `_generate` for token tracking, and export the class from [`tradingagents/llm_adapters/__init__.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/__init__.py).**

The TradingAgents-CN repository isolates every large language model behind a standardized adapter interface, allowing seamless swapping between providers like DashScope and Google without changing business logic. When you need to integrate a proprietary or niche LLM service, you must add a new LLM adapter for a custom provider in TradingAgents-CN following the canonical five-component pattern established in the codebase. This guide walks through the exact file locations, method signatures, and token-tracking requirements derived from the reference implementations.

## Understanding the Adapter Architecture in TradingAgents-CN

Every LLM adapter in TradingAgents-CN follows a strict five-component contract to ensure consistent behavior across providers. Located in `tradingagents/llm_adapters/`, each module subclasses a LangChain base—typically `ChatOpenAI` for OpenAI-compatible endpoints—and implements provider-specific initialization, token tracking, and factory utilities.

The five mandatory components are:

- **Base class inheritance** – Subclass `ChatOpenAI` (or another LangChain chat model) to inherit standard invocation patterns.
- **Initialization logic** – Resolve API keys from database configuration or environment variables, set sensible defaults for `temperature`, `max_tokens`, and `base_url`, and validate credentials.
- **Token tracking** – Override `_generate` to intercept usage statistics and forward them to the central `token_tracker` from `tradingagents.config.config_manager`.
- **Factory helpers** – Provide `create_<provider>_llm`, `get_available_<provider>_models`, and optional `test_*` functions for quick instantiation and validation.
- **Module export** – Register the adapter in [`tradingagents/llm_adapters/__init__.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/__init__.py) so it is importable as `from tradingagents.llm_adapters import ChatMyProvider`.

Reference implementations demonstrate this pattern clearly. In [`tradingagents/llm_adapters/dashscope_openai_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/dashscope_openai_adapter.py), the class header and inheritance appear at lines 19–20, the `__init__` method handling API key resolution and defaults spans lines 28–79, and the token-tracking `_generate` override occupies lines 102–136.

## Step-by-Step Guide to Adding Your Custom LLM Adapter

### Step 1: Create the Adapter Module

Create a new Python file at [`tradingagents/llm_adapters/myprovider_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/myprovider_adapter.py). This file will house the adapter class and all supporting utilities for your custom provider.

### Step 2: Import Core Dependencies

At the top of the module, import the required LangChain classes, typing utilities, and TradingAgents-CN internals:

```python
import os
from typing import Any, Dict, List, Optional

from langchain_openai import ChatOpenAI
from langchain_core.tools import BaseTool

from tradingagents.utils.logging_manager import get_logger
from tradingagents.config.config_manager import token_tracker

```

### Step 3: Define the Adapter Class

Subclass `ChatOpenAI` (or the appropriate LangChain base for your provider) and provide a docstring:

```python
class ChatMyProvider(ChatOpenAI):
    """MyProvider OpenAI-compatible adapter with token tracking."""

```

### Step 4: Implement Initialization and Configuration

Override `__init__` to handle provider-specific setup. Follow the pattern from [`dashscope_openai_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/dashscope_openai_adapter.py) lines 28–79:

- Log initialization start using `get_logger(__name__)`.
- Resolve the API key from `kwargs` or the `MYPROVIDER_API_KEY` environment variable.
- Set defaults for `base_url`, `model`, `temperature`, and `max_tokens` using `kwargs.setdefault(...)`.
- Validate the API key and raise `ValueError` if missing.
- Call `super().__init__(**kwargs)` to complete LangChain initialization.

### Step 5: Add Token Tracking

Override the `_generate` method to intercept usage statistics and forward them to the central tracker, mirroring lines 102–136 of the DashScope adapter:

```python
def _generate(self, messages, stop=None, run_manager=None, **kwargs):
    result = super()._generate(messages, stop=run_manager, **kwargs)
    
    if result.llm_output and "token_usage" in result.llm_output:
        usage = result.llm_output["token_usage"]
        session_id = kwargs.get("session_id", "default")
        analysis_type = kwargs.get("analysis_type", "general")
        
        token_tracker.track_usage(
            provider="myprovider",
            model=self.model_name,
            input_tokens=usage.get("prompt_tokens", 0),
            output_tokens=usage.get("completion_tokens", 0),
            session_id=session_id,
            analysis_type=analysis_type
        )
    
    return result

```

### Step 6: Define Model Metadata and Factory Helpers

Create a dictionary mapping model names to capabilities, then provide a factory function:

```python
MYPROVIDER_MODELS = {
    "my-model": {
        "description": "MyProvider standard model",
        "context_length": 8192,
        "supports_function_calling": True,
        "recommended_for": ["analysis", "trading"]
    }
}

def create_myprovider_llm(
    model: str = "my-model",
    api_key: Optional[str] = None,
    temperature: float = 0.1,
    max_tokens: int = 2000,
    **kwargs
) -> ChatMyProvider:
    """Factory function to instantiate MyProvider LLM."""
    return ChatMyProvider(
        model=model,
        api_key=api_key,
        temperature=temperature,
        max_tokens=max_tokens,
        **kwargs
    )

def get_available_myprovider_models() -> Dict[str, Any]:
    """Return metadata for available MyProvider models."""
    return MYPROVIDER_MODELS

```

### Step 7: Register the Adapter

Open [`tradingagents/llm_adapters/__init__.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/__init__.py) and add the import:

```python
from .myprovider_adapter import ChatMyProvider

__all__.append("ChatMyProvider")

```

This makes the adapter available via `from tradingagents.llm_adapters import ChatMyProvider`.

## Complete Working Example

Here is a minimal, runnable example demonstrating how to use the newly created adapter once registered:

```python

# example_usage.py

from tradingagents.llm_adapters import ChatMyProvider

# 1️⃣ Create an LLM instance (API key can be omitted if set in env var MYPROVIDER_API_KEY)

llm = ChatMyProvider(
    model="my-model",
    temperature=0.2,
    max_tokens=500,
    # You can also pass a custom endpoint:

    # base_url="https://api.myprovider.com/v1"

)

# 2️⃣ Simple invoke

response = llm.invoke("请介绍一下今天的AAPL股价走势。")
print("模型回复:", response.content)

# 3️⃣ Function‑calling example (assuming the provider supports it)

from langchain_core.tools import tool

@tool
def get_stock_price(symbol: str) -> str:
    """返回给定股票代码的当前价格（模拟实现）。"""
    return f"{symbol} 当前价: 123.45 USD"

llm_with_tool = llm.bind_tools([get_stock_price])
result = llm_with_tool.invoke("请帮我查询下 AAPL 的最新价格")
print(result)          # May contain `tool_calls` if the model decides to use the tool

```

Running this script logs initialization steps via the adapter's logger, automatically tracks token usage through `token_tracker`, and allows seamless provider swapping by changing only the import line.

## Key Reference Files and Implementation Details

Study these specific files and line ranges to understand the exact implementation patterns:

| File | Purpose | Critical Sections |
|------|---------|-------------------|
| [`tradingagents/llm_adapters/__init__.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/__init__.py) | Public export of adapters | Lines 1‑5 demonstrate how adapters are re-exported for clean imports |
| [`tradingagents/llm_adapters/dashscope_openai_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/dashscope_openai_adapter.py) | Reference for OpenAI-compatible providers | *Inheritance* (lines 19‑20)<br>*`__init__` with API key handling* (lines 28‑79)<br>*Token tracking in `_generate`* (lines 102‑136) |
| [`tradingagents/llm_adapters/google_openai_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/google_openai_adapter.py) | Reference for non-OpenAI providers | *Custom base class handling* and `base_url` configuration (lines 21‑38) |
| [`tradingagents/config/config_manager.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/config/config_manager.py) | Central token tracking infrastructure | Provides `token_tracker` used by all adapters for usage analytics |

## Summary

To successfully add a new LLM adapter for a custom provider in TradingAgents-CN, follow these core principles:

- **Isolate provider logic** in a dedicated module under `tradingagents/llm_adapters/` using the five-component structure.
- **Inherit from LangChain bases** like `ChatOpenAI` to maintain compatibility with the existing tool-calling and invocation patterns.
- **Validate credentials early** in `__init__`, supporting both explicit arguments and environment variables with clear error messages.
- **Track every token** by overriding `_generate` and forwarding usage statistics to `token_tracker` from `tradingagents.config.config_manager`.
- **Expose clean factory functions** like `create_myprovider_llm` and register the class in [`tradingagents/llm_adapters/__init__.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/tradingagents/llm_adapters/__init__.py) for discoverability.

## Frequently Asked Questions

### What base class should I use when adding a new LLM adapter for a custom provider in TradingAgents-CN?

Use `ChatOpenAI` from `langchain_openai` if your provider offers an OpenAI-compatible API. For providers with unique protocols, subclass the appropriate LangChain class (such as `ChatGoogleGenerativeAI` for Google models) and normalize the interface within your adapter. The existing [`google_openai_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/google_openai_adapter.py) demonstrates how to handle non-OpenAI bases while maintaining compatibility.

### How does token tracking work in custom TradingAgents-CN LLM adapters?

Token tracking relies on overriding the `_generate` method in your adapter class. After calling `super()._generate()`, inspect `result.llm_output["token_usage"]` to extract `prompt_tokens` and `completion_tokens`. Forward these values along with the provider name and model identifier to `token_tracker.track_usage()` from `tradingagents.config.config_manager`. This pattern appears in [`dashscope_openai_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/dashscope_openai_adapter.py) lines 102–136.

### Where should I store API keys for a custom LLM provider in TradingAgents-CN?

Resolve API keys using a cascading priority: first check the `api_key` argument passed to `__init__`, then fall back to environment variables (e.g., `MYPROVIDER_API_KEY`), and optionally check the database configuration if your adapter integrates with the project's config manager. Always validate the key exists before calling `super().__init__()` and raise a descriptive `ValueError` if credentials are missing, following the validation logic in [`dashscope_openai_adapter.py`](https://github.com/hsliuping/TradingAgents-CN/blob/main/dashscope_openai_adapter.py) lines 34–68.

### Can I use function calling and tools with a custom LLM adapter in TradingAgents-CN?

Yes, provided your underlying provider supports function calling. Inherit from `ChatOpenAI` (which supports tool binding) and ensure your model metadata dictionary includes `"supports_function_calling": True`. Users can then call `llm.bind_tools([...])` and invoke the model with tool definitions. Test this capability by implementing a `test_myprovider_function_calling` helper function in your adapter module, following the validation patterns in existing adapters.