How to Add a New LLM Adapter for a Custom Provider in TradingAgents-CN
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.
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, andbase_url, and validate credentials. - Token tracking – Override
_generateto intercept usage statistics and forward them to the centraltoken_trackerfromtradingagents.config.config_manager. - Factory helpers – Provide
create_<provider>_llm,get_available_<provider>_models, and optionaltest_*functions for quick instantiation and validation. - Module export – Register the adapter in
tradingagents/llm_adapters/__init__.pyso it is importable asfrom tradingagents.llm_adapters import ChatMyProvider.
Reference implementations demonstrate this pattern clearly. In 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. 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:
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:
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 lines 28–79:
- Log initialization start using
get_logger(__name__). - Resolve the API key from
kwargsor theMYPROVIDER_API_KEYenvironment variable. - Set defaults for
base_url,model,temperature, andmax_tokensusingkwargs.setdefault(...). - Validate the API key and raise
ValueErrorif 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:
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:
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 and add the import:
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:
# 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 |
Public export of adapters | Lines 1‑5 demonstrate how adapters are re-exported for clean imports |
tradingagents/llm_adapters/dashscope_openai_adapter.py |
Reference for OpenAI-compatible providers | Inheritance (lines 19‑20)__init__ with API key handling (lines 28‑79)Token tracking in _generate (lines 102‑136) |
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 |
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
ChatOpenAIto 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
_generateand forwarding usage statistics totoken_trackerfromtradingagents.config.config_manager. - Expose clean factory functions like
create_myprovider_llmand register the class intradingagents/llm_adapters/__init__.pyfor 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 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 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 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.
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 →