This commit is contained in:
parent
91ad37412b
commit
02b83a6ddd
237
README.md
237
README.md
|
|
@ -21,16 +21,70 @@
|
|||
### 🎯 核心特色
|
||||
|
||||
- 🤖 **多代理協作架構** - 專業化的 AI 代理團隊協同工作
|
||||
- 🌐 **多模型靈活支援** - 支援 OpenAI、Anthropic、Grok、DeepSeek、Qwen 等多家 LLM 提供商
|
||||
- 🔧 **自訂端點配置** - 完整支援自訂 API 端點,可連接任何 OpenAI 兼容的服務
|
||||
- 📊 **全方位市場分析** - 整合技術面、基本面、情緒面、新聞面分析
|
||||
- 🔄 **結構化決策流程** - 透過看漲/看跌辯論機制減少偏見
|
||||
- 🧠 **長期記憶系統** - 使用 ChromaDB 向量數據庫儲存歷史決策
|
||||
- 🎨 **現代化 Web 介面** - 基於 Next.js 16 的響應式 UI
|
||||
- 🔌 **RESTful API** - 完整的後端 API 支援
|
||||
- 🐳 **一鍵部署** - 支援 Docker Compose 和 Railway 部署
|
||||
- 🐳 **一鍵部署** - 支援 Docker Compose 部署
|
||||
- 🔑 **BYOK (Bring Your Own Key)** - 使用者自帶 API 金鑰,保障隱私與成本控制
|
||||
|
||||
---
|
||||
|
||||
## 🤖 LLM 模型支援
|
||||
|
||||
TradingAgents 支援業界領先的多家 LLM 提供商,並為每個模型配置**獨立的 API Key 和 Base URL**,實現最大靈活性。
|
||||
|
||||
### 📋 支援的 LLM 提供商矩陣
|
||||
|
||||
| 提供商 | 支援模型 | Base URL | 是否支援自訂端點 |
|
||||
|--------|---------|----------|----------------|
|
||||
| **OpenAI** | GPT-5.1, GPT-5 Mini/Nano, GPT-4.1 Mini/Nano, o4-mini | `https://api.openai.com/v1` | ✅ 是 |
|
||||
| **Anthropic** | Claude Haiku 4.5, Claude Sonnet 4.5/4.0, Claude 3.5 Haiku, Claude 3 Haiku | `https://api.anthropic.com/` | ✅ 是 |
|
||||
| **Grok (xAI)** | Grok-4.1 Fast, Grok-4 Fast, Grok-4, Grok-3, Grok-3 Mini | `https://api.x.ai/v1` | ✅ 是 |
|
||||
| **DeepSeek** | DeepSeek Reasoner, DeepSeek Chat | `https://api.deepseek.com` | ✅ 是 |
|
||||
| **Qwen (Alibaba)** | Qwen3-Max, Qwen-Plus | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | ✅ 是 |
|
||||
| **自訂端點** | 任何 OpenAI 兼容的 API | 使用者自訂 | ✅ 完全支援 |
|
||||
|
||||
### 🔧 三層獨立配置
|
||||
|
||||
系統支援**三個獨立的 LLM 配置點**,每個都可使用不同的提供商和 API Key:
|
||||
|
||||
#### 1️⃣ 快速思維模型 (Quick Thinking)
|
||||
用於快速分析和即時回應(市場分析師、情緒分析師等)
|
||||
|
||||
#### 2️⃣ 深層思維模型 (Deep Thinking)
|
||||
用於複雜推理和深度分析(研究團隊辯論、風險管理等)
|
||||
|
||||
#### 3️⃣ 嵌入模型 (Embedding)
|
||||
用於向量記憶體系統(ChromaDB 嵌入生成)
|
||||
|
||||
**配置示例:**
|
||||
```yaml
|
||||
快速思維: OpenAI GPT-5 Mini @ api.openai.com
|
||||
深層思維: Anthropic Claude Sonnet 4.5 @ api.anthropic.com
|
||||
嵌入模型: 自訂端點 @ your-custom-endpoint.com
|
||||
```
|
||||
|
||||
### 🌍 自訂端點支援
|
||||
|
||||
**完整支援自訂 API 端點**,任何實現 OpenAI Chat Completions API 規範的服務都可以使用:
|
||||
|
||||
✅ **支援場景:**
|
||||
- 私有化部署的 LLM 服務
|
||||
- 第三方 OpenAI 兼容代理
|
||||
- 本地運行的 LLM(如 Ollama、LocalAI)
|
||||
- 企業內部 AI Gateway
|
||||
|
||||
**配置方式:**
|
||||
1. 在前端表單的 Base URL 輸入框直接輸入自訂 URL
|
||||
2. 填入對應的 API Key
|
||||
3. 系統自動使用您的端點進行推理
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 系統架構
|
||||
|
||||
TradingAgents 採用前後端分離架構,後端使用 FastAPI 提供 RESTful API,前端使用 Next.js 打造現代化的使用者介面。
|
||||
|
|
@ -48,14 +102,14 @@ TradingAgents/
|
|||
│ │ ├── routes.py # API 端點定義
|
||||
│ │ └── dependencies.py # 依賴注入
|
||||
│ ├── core/ # 核心配置
|
||||
│ │ ├── config.py # 環境變數與設定
|
||||
│ │ ├──config.py # 環境變數與設定
|
||||
│ │ └── cors.py # CORS 中間件配置
|
||||
│ ├── models/ # 資料模型
|
||||
│ │ └── schemas.py # Pydantic 資料結構
|
||||
│ └── services/ # 業務邏輯層
|
||||
│ ├── trading_service.py # TradingAgents 核心整合
|
||||
│ └── price_service.py # 股價資料處理服務
|
||||
│
|
||||
│ └── task_manager.py # 異步任務管理
|
||||
|
||||
├── frontend/ # Next.js 前端應用
|
||||
│ ├── app/ # Next.js App Router
|
||||
│ │ ├── layout.tsx # 根佈局組件
|
||||
|
|
@ -72,10 +126,7 @@ TradingAgents/
|
|||
│ │ ├── layout/ # 佈局組件
|
||||
│ │ │ ├── Header.tsx # 頂部導航欄
|
||||
│ │ │ └── Footer.tsx # 頁腳
|
||||
│ │ ├── shared/ # 共用組件
|
||||
│ │ └── ui/ # shadcn/ui 基礎組件
|
||||
│ ├── context/ # React Context API
|
||||
│ │ └── AnalysisContext.tsx # 分析狀態管理
|
||||
│ ├── hooks/ # 自定義 React Hooks
|
||||
│ │ ├── useAnalysis.ts # 分析請求管理
|
||||
│ │ └── useConfig.ts # 配置資料獲取
|
||||
|
|
@ -83,7 +134,7 @@ TradingAgents/
|
|||
│ ├── api.ts # API 客戶端封裝
|
||||
│ ├── types.ts # TypeScript 型別定義
|
||||
│ └── utils.ts # 通用輔助函式
|
||||
│
|
||||
|
||||
└── tradingagents/ # 核心 Python 套件
|
||||
├── agents/ # AI 代理定義
|
||||
├── dataflows/ # 資料流處理
|
||||
|
|
@ -103,6 +154,7 @@ TradingAgents/
|
|||
| **yfinance** | 股票市場資料獲取 | ≥0.2.63 |
|
||||
| **Uvicorn** | ASGI 伺服器 | ≥0.24.0 |
|
||||
| **python-dotenv** | 環境變數管理 | 1.0.0 |
|
||||
| **Redis** | 任務隊列與緩存 | Latest |
|
||||
|
||||
#### 其他整合
|
||||
- **stockstats**: 技術指標計算
|
||||
|
|
@ -141,9 +193,19 @@ TradingAgents/
|
|||
|
||||
#### 必要的 API 金鑰
|
||||
|
||||
- **OpenAI API Key** (必需) - 用於驅動 AI 代理
|
||||
根據您選擇的 LLM 提供商,準備相應的 API 金鑰:
|
||||
|
||||
- **OpenAI API Key** - GPT 系列模型
|
||||
- 申請網址: https://platform.openai.com/api-keys
|
||||
- **Alpha Vantage API Key** (可選) - 用於更詳細的股票資料
|
||||
- **Anthropic API Key** - Claude 系列模型
|
||||
- 申請網址: https://console.anthropic.com
|
||||
- **Grok API Key** - Grok 系列模型
|
||||
- 申請網址: https://console.x.ai
|
||||
- **DeepSeek API Key** - DeepSeek 系列模型
|
||||
- 申請網址: https://platform.deepseek.com
|
||||
- **Qwen API Key** - Qwen 系列模型
|
||||
- 申請網址: https://www.alibabacloud.com
|
||||
- **Alpha Vantage API Key** (必需) - 股票基本面資料
|
||||
- 申請網址: https://www.alphavantage.co/support/#api-key
|
||||
|
||||
> 💡 **提示**: 本系統採用 BYOK (Bring Your Own Key) 模式,您可以在前端介面直接輸入 API 金鑰,無需設定環境變數(適合快速測試)。
|
||||
|
|
@ -185,7 +247,7 @@ pip install -e .
|
|||
pip install -r backend/requirements.txt
|
||||
```
|
||||
|
||||
##### 2.3 配置環境變數
|
||||
##### 2.3 配置環境變數(可選)
|
||||
|
||||
複製範例環境變數檔案並編輯:
|
||||
|
||||
|
|
@ -196,19 +258,25 @@ cp .env.example .env
|
|||
編輯 `.env` 檔案,填入您的 API 金鑰:
|
||||
|
||||
```bash
|
||||
# ============ API 金鑰配置 ============
|
||||
# OpenAI API (必需)
|
||||
# ============ LLM API 金鑰配置 ============
|
||||
# OpenAI (可選 - 可在前端直接輸入)
|
||||
OPENAI_API_KEY=sk-your-openai-api-key-here
|
||||
OPENAI_BASE_URL=https://api.openai.com/v1
|
||||
|
||||
# Alpha Vantage API (可選)
|
||||
# Anthropic Claude (可選)
|
||||
ANTHROPIC_API_KEY=sk-ant-your-claude-key
|
||||
|
||||
# Grok / xAI (可選)
|
||||
XAI_API_KEY=your-grok-key
|
||||
|
||||
# DeepSeek (可選)
|
||||
DEEPSEEK_API_KEY=your-deepseek-key
|
||||
|
||||
# Qwen / Alibaba Cloud (可選)
|
||||
DASHSCOPE_API_KEY=your-qwen-key
|
||||
|
||||
# Alpha Vantage (強烈建議 - 用於基本面數據)
|
||||
ALPHA_VANTAGE_API_KEY=your-alpha-vantage-key
|
||||
|
||||
# 其他 LLM 提供商 (可選)
|
||||
ANTHROPIC_API_KEY=your-claude-api-key
|
||||
GOOGLE_API_KEY=your-gemini-api-key
|
||||
GOOGLE_API_KEY=your-gemini-api-key
|
||||
|
||||
# ============ 後端服務配置 ============
|
||||
BACKEND_HOST=0.0.0.0
|
||||
BACKEND_PORT=8000
|
||||
|
|
@ -220,6 +288,8 @@ CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
|
|||
TRADINGAGENTS_RESULTS_DIR=./results
|
||||
```
|
||||
|
||||
> 📝 **注意**: 環境變數中的 API Key 為可選配置。您可以在前端表單中直接輸入,系統會優先使用前端輸入的 Key。
|
||||
|
||||
##### 2.4 啟動後端服務
|
||||
|
||||
```bash
|
||||
|
|
@ -276,17 +346,27 @@ npm --prefix frontend run dev
|
|||
|
||||
最簡單的部署方式,一鍵啟動前後端服務:
|
||||
|
||||
**前置要求:**
|
||||
- Docker Engine 20.10+
|
||||
- Docker Compose V2
|
||||
|
||||
**部署步驟:**
|
||||
|
||||
```bash
|
||||
# 啟動所有服務(首次執行會自動構建映像)
|
||||
# 1. 確保 .env 文件已配置(至少包含 Alpha Vantage API Key)
|
||||
cp .env.example .env
|
||||
# 編輯 .env,填入必要的 API 金鑰
|
||||
|
||||
# 2. 啟動所有服務(首次執行會自動構建映像)
|
||||
docker compose up -d --build
|
||||
|
||||
# 查看服務運行狀態
|
||||
# 3. 查看服務運行狀態
|
||||
docker compose ps
|
||||
|
||||
# 查看即時日誌
|
||||
# 4. 查看即時日誌
|
||||
docker compose logs -f
|
||||
|
||||
# 查看特定服務日誌
|
||||
# 5. 查看特定服務日誌
|
||||
docker compose logs -f backend
|
||||
docker compose logs -f frontend
|
||||
|
||||
|
|
@ -301,6 +381,7 @@ docker compose down -v
|
|||
- 後端服務運行於: `http://localhost:8000`
|
||||
- 前端服務運行於: `http://localhost:3000`
|
||||
- 分析結果會持久化儲存在 `./results` 目錄
|
||||
- 環境變數從 `.env` 文件自動載入
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -318,6 +399,8 @@ docker compose down -v
|
|||
|
||||
3. **配置分析參數**
|
||||
|
||||
#### 📊 基本設定
|
||||
|
||||
- **選擇分析師團隊**: 勾選您需要的分析師類型
|
||||
- ✅ 市場分析師 (Market Analyst) - 技術分析與價格走勢
|
||||
- ✅ 情緒分析師 (Sentiment Analyst) - 社交媒體情緒評估
|
||||
|
|
@ -335,31 +418,44 @@ docker compose down -v
|
|||
- 🟡 **中等 (Medium)**: 平衡速度與深度
|
||||
- 🔴 **深層 (Deep)**: 全面深入分析,耗時較長
|
||||
|
||||
- **選擇 LLM 模型**:
|
||||
|
||||
系統提供兩種類型的模型配置:
|
||||
|
||||
**快速思維模型** (用於快速分析和即時回應):
|
||||
- `gpt-5.1-2025-11-13` - GPT-5.1 (最新)
|
||||
- `gpt-5-mini-2025-08-07` - GPT-5 Mini (預設)
|
||||
- `gpt-5-nano-2025-08-07` - GPT-5 Nano
|
||||
- `gpt-4.1-mini` - GPT-4.1 Mini
|
||||
- `gpt-4.1-nano` - GPT-4.1 Nano
|
||||
- `o4-mini-2025-04-16` - o4-mini
|
||||
|
||||
**深層思維模型** (用於複雜推理和深度分析):
|
||||
- `gpt-5.1-2025-11-13` - GPT-5.1 (最新)
|
||||
- `gpt-5-mini-2025-08-07` - GPT-5 Mini (預設)
|
||||
- `gpt-5-nano-2025-08-07` - GPT-5 Nano
|
||||
- `gpt-4.1-mini` - GPT-4.1 Mini
|
||||
- `gpt-4.1-nano` - GPT-4.1 Nano
|
||||
- `o4-mini-2025-04-16` - o4-mini
|
||||
|
||||
> 💡 **提示**: 快速思維模型用於初步分析和資料收集,深層思維模型用於複雜決策和策略制定。您可以根據需求選擇不同的模型組合。
|
||||
#### 🤖 LLM 模型配置
|
||||
|
||||
- **輸入 API 金鑰**:
|
||||
- 在表單中直接輸入您的 OpenAI API Key
|
||||
- 或使用環境變數預設值(如已配置)
|
||||
系統提供**三個獨立的 LLM 配置選項**,每個都可使用不同的提供商:
|
||||
|
||||
**1. 快速思維模型配置**
|
||||
- **模型選擇**: 從下拉選單選擇模型(OpenAI, Anthropic, Grok, DeepSeek, Qwen)
|
||||
- **Base URL**: 直接輸入自訂端點 URL(例如:`https://api.your-custom-endpoint.com/v1`)
|
||||
- **API Key**: 輸入對應的 API 金鑰
|
||||
|
||||
**2. 深層思維模型配置**
|
||||
- **模型選擇**: 可選擇與快速思維不同的模型
|
||||
- **Base URL**: 支援不同的端點
|
||||
- **API Key**: 支援不同的金鑰
|
||||
|
||||
**3. 嵌入模型配置**
|
||||
- **Base URL**: 下拉選擇 OpenAI 或自訂端點
|
||||
- **API Key**: 若留空則使用環境變數 `OPENAI_API_KEY`
|
||||
|
||||
**配置示例:**
|
||||
```
|
||||
快速思維模型: gpt-5-mini-2025-08-07
|
||||
快速思維 Base URL: https://api.openai.com/v1
|
||||
快速思維 API Key: sk-your-openai-key
|
||||
|
||||
深層思維模型: claude-sonnet-4-5-20250929
|
||||
深層思維 Base URL: https://api.anthropic.com/
|
||||
深層思維 API Key: sk-ant-your-claude-key
|
||||
|
||||
嵌入模型 Base URL: 自訂 → https://api.your-embedding-service.com/v1
|
||||
嵌入模型 API Key: your-embedding-key
|
||||
```
|
||||
|
||||
> 💡 **靈活性**: 您可以混合使用不同提供商的模型,例如用 OpenAI 做快速分析,用 Claude 做深度推理,用自訂端點做嵌入生成。
|
||||
|
||||
#### 🔑 API 金鑰配置
|
||||
|
||||
- **Alpha Vantage API Key** (必填): 用於獲取股票基本面數據
|
||||
- 如未在環境變數中配置 LLM API Key,需在此填入
|
||||
|
||||
4. **執行分析**
|
||||
- 檢查所有參數無誤後,點擊「執行分析」按鈕
|
||||
|
|
@ -399,7 +495,7 @@ docker compose down -v
|
|||
curl http://localhost:8000/api/health
|
||||
```
|
||||
|
||||
#### 執行股票分析
|
||||
#### 執行股票分析(使用自訂端點)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/analyze \
|
||||
|
|
@ -407,10 +503,17 @@ curl -X POST http://localhost:8000/api/analyze \
|
|||
-d '{
|
||||
"ticker": "NVDA",
|
||||
"analysis_date": "2024-01-15",
|
||||
"research_depth": "medium",
|
||||
"model": "gpt-5-mini-2025-08-07",
|
||||
"selected_analysts": ["market", "sentiment", "news", "fundamental"],
|
||||
"api_key": "sk-your-openai-key"
|
||||
"research_depth": 2,
|
||||
"deep_think_llm": "claude-sonnet-4-5-20250929",
|
||||
"quick_think_llm": "gpt-5-mini-2025-08-07",
|
||||
"analysts": ["market", "sentiment", "news", "fundamental"],
|
||||
"quick_think_base_url": "https://api.openai.com/v1",
|
||||
"deep_think_base_url": "https://api.anthropic.com/",
|
||||
"embedding_base_url": "https://api.openai.com/v1",
|
||||
"quick_think_api_key": "sk-your-openai-key",
|
||||
"deep_think_api_key": "sk-ant-your-claude-key",
|
||||
"embedding_api_key": "sk-your-embedding-key",
|
||||
"alpha_vantage_api_key": "your-alpha-vantage-key"
|
||||
}'
|
||||
```
|
||||
|
||||
|
|
@ -492,31 +595,25 @@ TradingAgents 模擬真實交易公司的組織架構,每個代理都有其專
|
|||
### 智能特性
|
||||
|
||||
#### 1. 動態研究深度調整
|
||||
- **Shallow**: 每個代理進行 1 輪分析,適合快速決策
|
||||
- **Medium**: 每個代理進行 2-3 輪分析,平衡深度與速度
|
||||
- **Deep**: 每個代理進行 5+ 輪分析,全面深入研究
|
||||
- **Shallow (1)**: 每個代理進行 1 輪分析,適合快速決策
|
||||
- **Medium (2)**: 每個代理進行 2 輪分析,平衡深度與速度
|
||||
- **Deep (3+)**: 每個代理進行 3+ 輪分析,全面深入研究
|
||||
|
||||
#### 2. 多模型支持
|
||||
- 支援 OpenAI (GPT-4, GPT-4o, o1 系列)
|
||||
- 支援 Anthropic Claude
|
||||
- 支援 Google Gemini
|
||||
- 支援 Google Gemini
|
||||
|
||||
#### 3. 長期記憶系統
|
||||
#### 2. 長期記憶系統
|
||||
- 使用 ChromaDB 向量資料庫儲存歷史決策
|
||||
- 代理可以參考過去類似情況的決策
|
||||
- 持續學習與改進分析品質
|
||||
|
||||
#### 4. 結構化輸出
|
||||
#### 3. 結構化輸出
|
||||
- 所有報告均採用 Markdown 格式
|
||||
- 清晰的章節結構
|
||||
- 支援表格、列表、程式碼區塊等豐富格式
|
||||
|
||||
#### 5. 實時資料整合
|
||||
#### 4. 實時資料整合
|
||||
- yfinance: 即時股價與歷史資料
|
||||
- Reddit API: 社群情緒分析
|
||||
- RSS Feeds: 財經新聞抓取
|
||||
- Alpha Vantage: 詳細財務資料(可選)
|
||||
- Alpha Vantage: 詳細財務資料(必需)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -530,7 +627,7 @@ TradingAgents 模擬真實交易公司的組織架構,每個代理都有其專
|
|||
|
||||
### 分析配置頁面
|
||||
|
||||
直觀的表單介面,輕鬆配置所有分析參數
|
||||
直觀的表單介面,輕鬆配置所有分析參數(包含多模型和自訂端點配置)
|
||||
|
||||

|
||||
|
||||
|
|
@ -589,3 +686,9 @@ TradingAgents 模擬真實交易公司的組織架構,每個代理都有其專
|
|||
- [shadcn/ui](https://github.com/shadcn/ui) - 精美的 React 組件庫
|
||||
- [ChromaDB](https://github.com/chroma-core/chroma) - AI 原生向量資料庫
|
||||
- [yfinance](https://github.com/ranaroussi/yfinance) - Yahoo Finance 資料下載工具
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
本專案採用 Apache 2.0 許可證 - 查看 [LICENSE](LICENSE) 文件了解詳情。
|
||||
|
|
|
|||
|
|
@ -93,6 +93,12 @@ async def run_analysis(
|
|||
quick_think_llm=request.quick_think_llm,
|
||||
openai_api_key=request.openai_api_key or "", # Pass empty string if None, service handles it
|
||||
openai_base_url=request.openai_base_url,
|
||||
quick_think_base_url=request.quick_think_base_url,
|
||||
deep_think_base_url=request.deep_think_base_url,
|
||||
quick_think_api_key=request.quick_think_api_key or "",
|
||||
deep_think_api_key=request.deep_think_api_key or "",
|
||||
embedding_base_url=request.embedding_base_url,
|
||||
embedding_api_key=request.embedding_api_key or "",
|
||||
alpha_vantage_api_key=request.alpha_vantage_api_key,
|
||||
))
|
||||
|
||||
|
|
|
|||
|
|
@ -22,11 +22,27 @@ class AnalysisRequest(BaseModel):
|
|||
openai_api_key: Optional[str] = Field(None, description="OpenAI API Key (optional if set on server)", min_length=0)
|
||||
openai_base_url: Optional[str] = Field(
|
||||
default="https://api.openai.com/v1",
|
||||
description="OpenAI API Base URL (optional)"
|
||||
description="OpenAI API Base URL (optional, deprecated)"
|
||||
)
|
||||
alpha_vantage_api_key: Optional[str] = Field(
|
||||
None,
|
||||
description="Alpha Vantage API Key (optional, for enhanced data)"
|
||||
quick_think_base_url: Optional[str] = Field(
|
||||
default="https://api.openai.com/v1",
|
||||
description="Base URL for Quick Thinking Model"
|
||||
)
|
||||
deep_think_base_url: Optional[str] = Field(
|
||||
default="https://api.openai.com/v1",
|
||||
description="Base URL for Deep Thinking Model"
|
||||
)
|
||||
quick_think_api_key: Optional[str] = Field(None, description="API Key for Quick Thinking Model", min_length=0)
|
||||
deep_think_api_key: Optional[str] = Field(None, description="API Key for Deep Thinking Model", min_length=0)
|
||||
embedding_base_url: Optional[str] = Field(
|
||||
default="https://api.openai.com/v1",
|
||||
description="Base URL for Embedding Model"
|
||||
)
|
||||
embedding_api_key: Optional[str] = Field(None, description="API Key for Embedding Model", min_length=0)
|
||||
alpha_vantage_api_key: str = Field(
|
||||
...,
|
||||
description="Alpha Vantage API Key (required for fundamental data)",
|
||||
min_length=1
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,14 @@ class TradingService:
|
|||
self,
|
||||
ticker: str,
|
||||
analysis_date: str,
|
||||
openai_api_key: str,
|
||||
openai_api_key: Optional[str] = None,
|
||||
openai_base_url: str = "https://api.openai.com/v1",
|
||||
quick_think_base_url: str = "https://api.openai.com/v1",
|
||||
deep_think_base_url: str = "https://api.openai.com/v1",
|
||||
quick_think_api_key: Optional[str] = None,
|
||||
deep_think_api_key: Optional[str] = None,
|
||||
embedding_base_url: str = "https://api.openai.com/v1",
|
||||
embedding_api_key: Optional[str] = None,
|
||||
alpha_vantage_api_key: Optional[str] = None,
|
||||
analysts: Optional[List[str]] = None,
|
||||
research_depth: int = 1,
|
||||
|
|
@ -57,7 +63,9 @@ class TradingService:
|
|||
ticker: Stock ticker symbol
|
||||
analysis_date: Date in YYYY-MM-DD format
|
||||
openai_api_key: OpenAI API Key (required)
|
||||
openai_base_url: OpenAI API Base URL (optional)
|
||||
openai_base_url: OpenAI API Base URL (optional, deprecated)
|
||||
quick_think_base_url: Base URL for Quick Thinking Model
|
||||
deep_think_base_url: Base URL for Deep Thinking Model
|
||||
alpha_vantage_api_key: Alpha Vantage API Key (optional)
|
||||
analysts: List of analyst types to include
|
||||
research_depth: Research depth (1-5)
|
||||
|
|
@ -78,13 +86,7 @@ class TradingService:
|
|||
original_alpha_key = os.environ.get("ALPHA_VANTAGE_API_KEY")
|
||||
|
||||
try:
|
||||
# Set API keys for this request
|
||||
if openai_api_key:
|
||||
os.environ["OPENAI_API_KEY"] = openai_api_key
|
||||
elif not os.environ.get("OPENAI_API_KEY"):
|
||||
# If no key provided and no env var, this will fail later, but let's log it
|
||||
logger.warning("No OpenAI API key provided in request or environment")
|
||||
|
||||
# Set Alpha Vantage API key if provided
|
||||
if alpha_vantage_api_key:
|
||||
os.environ["ALPHA_VANTAGE_API_KEY"] = alpha_vantage_api_key
|
||||
|
||||
|
|
@ -92,9 +94,33 @@ class TradingService:
|
|||
logger.info(f"Initializing TradingAgents for {ticker} on {analysis_date}")
|
||||
config = self.create_config(research_depth, deep_think_llm, quick_think_llm)
|
||||
|
||||
# Normalize base URLs (ensure lowercase paths, common issue with custom endpoints)
|
||||
def normalize_base_url(url: str) -> str:
|
||||
"""Normalize base URL to ensure proper formatting"""
|
||||
if url:
|
||||
# Replace common case variations
|
||||
url = url.replace("/V1", "/v1")
|
||||
url = url.replace("/V2", "/v2")
|
||||
return url
|
||||
|
||||
# Override with user-provided settings
|
||||
config["llm_provider"] = "openai"
|
||||
config["backend_url"] = openai_base_url
|
||||
# Use specific base URLs if provided, otherwise fallback to openai_base_url
|
||||
config["quick_think_base_url"] = normalize_base_url(
|
||||
quick_think_base_url if quick_think_base_url != "https://api.openai.com/v1" else openai_base_url
|
||||
)
|
||||
config["deep_think_base_url"] = normalize_base_url(
|
||||
deep_think_base_url if deep_think_base_url != "https://api.openai.com/v1" else openai_base_url
|
||||
)
|
||||
# Set backend_url as a fallback
|
||||
config["backend_url"] = normalize_base_url(openai_base_url)
|
||||
|
||||
# Resolve API keys: Use specific key if provided, else fallback to openai_api_key (legacy/shared)
|
||||
# Note: For non-OpenAI providers, the user MUST provide the specific key if it differs from the shared one.
|
||||
config["quick_think_api_key"] = quick_think_api_key if quick_think_api_key else openai_api_key
|
||||
config["deep_think_api_key"] = deep_think_api_key if deep_think_api_key else openai_api_key
|
||||
config["embedding_base_url"] = normalize_base_url(embedding_base_url)
|
||||
config["embedding_api_key"] = embedding_api_key if embedding_api_key else openai_api_key
|
||||
|
||||
# Initialize TradingAgents graph
|
||||
graph = TradingAgentsGraph(analysts, config=config, debug=True)
|
||||
|
|
@ -183,10 +209,6 @@ class TradingService:
|
|||
"claude-sonnet-4-0",
|
||||
"claude-3-5-haiku-20241022",
|
||||
"claude-3-haiku-20240307",
|
||||
# Google
|
||||
"gemini-2.0-flash-lite",
|
||||
"gemini-2.0-flash",
|
||||
"gemini-2.5-flash-lite",
|
||||
# Grok
|
||||
"grok-4-1-fast-reasoning",
|
||||
"grok-4-1-fast-non-reasoning",
|
||||
|
|
|
|||
16
cli/utils.py
16
cli/utils.py
|
|
@ -197,9 +197,11 @@ def select_shallow_thinking_agent(provider) -> str:
|
|||
("Claude Haiku 3", "claude-3-haiku-20240307")
|
||||
],
|
||||
"google": [
|
||||
("Gemini 2.0 Flash-Lite", "gemini-2.0-flash-lite"),
|
||||
("Gemini 2.5 Pro", "gemini-2.5-pro"),
|
||||
("Gemini 2.5 Flash", "gemini-2.5-flash"),
|
||||
("Gemini 2.5 Flash Lite", "gemini-2.5-flash-lite"),
|
||||
("Gemini 2.0 Flash", "gemini-2.0-flash"),
|
||||
("Gemini 2.5 Flash Lite", "gemini-2.5-flash-lite")
|
||||
("Gemini 2.0 Flash-Lite", "gemini-2.0-flash-lite")
|
||||
],
|
||||
"Grok":[
|
||||
("Grok 4.1 Fast Reasoning","grok-4-1-fast-reasoning"),
|
||||
|
|
@ -215,7 +217,7 @@ def select_shallow_thinking_agent(provider) -> str:
|
|||
("DeepSeek Chat","deepseek-chat")
|
||||
],
|
||||
"Qwen":[
|
||||
("Qwen 3.5 Max", "qwen3-max"),
|
||||
("Qwen 3 Max", "qwen3-max"),
|
||||
("Qwen Plus", "qwen-plus")
|
||||
]
|
||||
}
|
||||
|
|
@ -279,9 +281,11 @@ def select_deep_thinking_agent(provider) -> str:
|
|||
("Claude Haiku 3", "claude-3-haiku-20240307")
|
||||
],
|
||||
"google": [
|
||||
("Gemini 2.0 Flash-Lite", "gemini-2.0-flash-lite"),
|
||||
("Gemini 2.5 Pro", "gemini-2.5-pro"),
|
||||
("Gemini 2.5 Flash", "gemini-2.5-flash"),
|
||||
("Gemini 2.5 Flash Lite", "gemini-2.5-flash-lite"),
|
||||
("Gemini 2.0 Flash", "gemini-2.0-flash"),
|
||||
("Gemini 2.5 Flash Lite", "gemini-2.5-flash-lite")
|
||||
("Gemini 2.0 Flash-Lite", "gemini-2.0-flash-lite")
|
||||
],
|
||||
"Grok":[
|
||||
("Grok 4.1 Fast Reasoning","grok-4-1-fast-reasoning"),
|
||||
|
|
@ -297,7 +301,7 @@ def select_deep_thinking_agent(provider) -> str:
|
|||
("DeepSeek Chat","deepseek-chat")
|
||||
],
|
||||
"Qwen":[
|
||||
("Qwen 3.5 Max", "qwen3-max"),
|
||||
("Qwen 3 Max", "qwen3-max"),
|
||||
("Qwen Plus", "qwen-plus")
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,9 +40,13 @@ const formSchema = z.object({
|
|||
deep_thinking_agent: z.string().min(1, "請選擇深層思維模型"),
|
||||
|
||||
// API Configuration
|
||||
openai_api_key: z.string().optional().or(z.literal("")),
|
||||
openai_base_url: z.string().url("請輸入有效的 URL").optional().or(z.literal("")),
|
||||
alpha_vantage_api_key: z.string().optional().or(z.literal("")),
|
||||
quick_think_base_url: z.string().url("請輸入有效的 URL").optional().or(z.literal("")),
|
||||
deep_think_base_url: z.string().url("請輸入有效的 URL").optional().or(z.literal("")),
|
||||
quick_think_api_key: z.string().optional().or(z.literal("")),
|
||||
deep_think_api_key: z.string().optional().or(z.literal("")),
|
||||
embedding_base_url: z.string().url("請輸入有效的 URL").optional().or(z.literal("")),
|
||||
embedding_api_key: z.string().optional().or(z.literal("")),
|
||||
alpha_vantage_api_key: z.string().min(1, "請輸入 Alpha Vantage API Key"),
|
||||
});
|
||||
|
||||
interface AnalysisFormProps {
|
||||
|
|
@ -67,8 +71,12 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
research_depth: 3, // 預設中等層級
|
||||
shallow_thinking_agent: "gpt-5-mini-2025-08-07",
|
||||
deep_thinking_agent: "gpt-5-mini-2025-08-07",
|
||||
openai_api_key: "",
|
||||
openai_base_url: "https://api.openai.com/v1",
|
||||
quick_think_base_url: "https://api.openai.com/v1",
|
||||
deep_think_base_url: "https://api.openai.com/v1",
|
||||
quick_think_api_key: "",
|
||||
deep_think_api_key: "",
|
||||
embedding_base_url: "https://api.openai.com/v1",
|
||||
embedding_api_key: "",
|
||||
alpha_vantage_api_key: "",
|
||||
},
|
||||
});
|
||||
|
|
@ -229,7 +237,10 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>快速思維模型</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="選擇模型" />
|
||||
|
|
@ -251,10 +262,6 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
<SelectItem value="claude-3-5-haiku-20241022">Anthropic: Claude 3.5 Haiku</SelectItem>
|
||||
<SelectItem value="claude-3-haiku-20240307">Anthropic: Claude 3 Haiku</SelectItem>
|
||||
|
||||
{/* Google */}
|
||||
<SelectItem value="gemini-2.0-flash-lite">Google: Gemini 2.0 Flash-Lite</SelectItem>
|
||||
<SelectItem value="gemini-2.0-flash">Google: Gemini 2.0 Flash</SelectItem>
|
||||
<SelectItem value="gemini-2.5-flash-lite">Google: Gemini 2.5 Flash Lite</SelectItem>
|
||||
|
||||
{/* Grok */}
|
||||
<SelectItem value="grok-4-1-fast-reasoning">Grok: 4.1 Fast Reasoning</SelectItem>
|
||||
|
|
@ -270,7 +277,7 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
<SelectItem value="deepseek-chat">DeepSeek: Chat</SelectItem>
|
||||
|
||||
{/* Qwen */}
|
||||
<SelectItem value="qwen3-max">Qwen: 3.5 Max</SelectItem>
|
||||
<SelectItem value="qwen3-max">Qwen: 3 Max</SelectItem>
|
||||
<SelectItem value="qwen-plus">Qwen: Plus</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
|
@ -288,7 +295,10 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>深層思維模型</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="選擇模型" />
|
||||
|
|
@ -310,10 +320,6 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
<SelectItem value="claude-3-5-haiku-20241022">Anthropic: Claude 3.5 Haiku</SelectItem>
|
||||
<SelectItem value="claude-3-haiku-20240307">Anthropic: Claude 3 Haiku</SelectItem>
|
||||
|
||||
{/* Google */}
|
||||
<SelectItem value="gemini-2.0-flash-lite">Google: Gemini 2.0 Flash-Lite</SelectItem>
|
||||
<SelectItem value="gemini-2.0-flash">Google: Gemini 2.0 Flash</SelectItem>
|
||||
<SelectItem value="gemini-2.5-flash-lite">Google: Gemini 2.5 Flash Lite</SelectItem>
|
||||
|
||||
{/* Grok */}
|
||||
<SelectItem value="grok-4-1-fast-reasoning">Grok: 4.1 Fast Reasoning</SelectItem>
|
||||
|
|
@ -329,7 +335,7 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
<SelectItem value="deepseek-chat">DeepSeek: Chat</SelectItem>
|
||||
|
||||
{/* Qwen */}
|
||||
<SelectItem value="qwen3-max">Qwen: 3.5 Max</SelectItem>
|
||||
<SelectItem value="qwen3-max">Qwen: 3 Max</SelectItem>
|
||||
<SelectItem value="qwen-plus">Qwen: Plus</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
|
@ -343,33 +349,17 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* API Configuration Section */}
|
||||
<div className="space-y-4 border-t pt-6 mt-6">
|
||||
<h3 className="text-lg font-semibold">API 配置</h3>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="openai_api_key"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>OpenAI API Key(若伺服器已設定則選填)</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="sk-..." {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
您的 OpenAI API Key(若未填寫將使用伺服器環境變數)
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* API Configuration Section */}
|
||||
<div className="space-y-4 border-t pt-6 mt-6">
|
||||
<h3 className="text-lg font-semibold">API 配置</h3>
|
||||
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="openai_base_url"
|
||||
name="quick_think_base_url"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>API Base URL</FormLabel>
|
||||
<FormLabel>快速思維模型 Base URL</FormLabel>
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
if (value !== "custom") {
|
||||
|
|
@ -377,7 +367,6 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
} else {
|
||||
field.onChange(""); // Clear value for custom input
|
||||
}
|
||||
// Store selection state if needed, or just rely on field value
|
||||
}}
|
||||
defaultValue={
|
||||
[
|
||||
|
|
@ -408,7 +397,6 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* Show input only when custom is selected or value is not in the list */}
|
||||
{(![
|
||||
"https://api.openai.com/v1",
|
||||
"https://api.anthropic.com/",
|
||||
|
|
@ -429,7 +417,179 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
)}
|
||||
|
||||
<FormDescription>
|
||||
選擇或輸入 LLM 服務的 API 基礎網址
|
||||
快速思維模型的 API 基礎網址
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="quick_think_api_key"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>快速思維模型 API Key</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="sk-..." {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
該模型的專屬 API Key(若留空則使用預設/環境變數)
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="deep_think_base_url"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>深層思維模型 Base URL</FormLabel>
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
if (value !== "custom") {
|
||||
field.onChange(value);
|
||||
} else {
|
||||
field.onChange(""); // Clear value for custom input
|
||||
}
|
||||
}}
|
||||
defaultValue={
|
||||
[
|
||||
"https://api.openai.com/v1",
|
||||
"https://api.anthropic.com/",
|
||||
"https://generativelanguage.googleapis.com/v1",
|
||||
"https://api.x.ai/v1",
|
||||
"https://api.deepseek.com",
|
||||
"https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
||||
].includes(field.value || "")
|
||||
? field.value
|
||||
: "custom"
|
||||
}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="選擇 API 端點" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="https://api.openai.com/v1">OpenAI (預設)</SelectItem>
|
||||
<SelectItem value="https://api.anthropic.com/">Anthropic</SelectItem>
|
||||
<SelectItem value="https://generativelanguage.googleapis.com/v1">Google Gemini</SelectItem>
|
||||
<SelectItem value="https://api.x.ai/v1">Grok (xAI)</SelectItem>
|
||||
<SelectItem value="https://api.deepseek.com">DeepSeek</SelectItem>
|
||||
<SelectItem value="https://dashscope-intl.aliyuncs.com/compatible-mode/v1">Qwen (Alibaba)</SelectItem>
|
||||
<SelectItem value="custom">自訂端點</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{(![
|
||||
"https://api.openai.com/v1",
|
||||
"https://api.anthropic.com/",
|
||||
"https://generativelanguage.googleapis.com/v1",
|
||||
"https://api.x.ai/v1",
|
||||
"https://api.deepseek.com",
|
||||
"https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
||||
].includes(field.value || "") || field.value === "") && (
|
||||
<div className="mt-2">
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="請輸入自訂 Base URL"
|
||||
value={field.value || ""}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormDescription>
|
||||
深層思維模型的 API 基礎網址
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="deep_think_api_key"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>深層思維模型 API Key</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="sk-..." {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
該模型的專屬 API Key(若留空則使用預設/環境變數)
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="embedding_base_url"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>嵌入模型 Base URL</FormLabel>
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
if (value !== "custom") {
|
||||
field.onChange(value);
|
||||
} else {
|
||||
field.onChange(""); // Clear value for custom input
|
||||
}
|
||||
}}
|
||||
defaultValue={
|
||||
field.value === "https://api.openai.com/v1" || !field.value
|
||||
? "https://api.openai.com/v1"
|
||||
: "custom"
|
||||
}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="選擇嵌入模型端點" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="https://api.openai.com/v1">OpenAI (預設)</SelectItem>
|
||||
<SelectItem value="custom">自訂端點</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{field.value !== "https://api.openai.com/v1" && (
|
||||
<div className="mt-2">
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="請輸入自訂 Base URL"
|
||||
value={field.value || ""}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormDescription>
|
||||
嵌入向量生成的 API 端點(用於記憶體系統)
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="embedding_api_key"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>嵌入模型 API Key</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="sk-..." {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
該端點的 API Key(若留空則使用環境變數 OPENAI_API_KEY)
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
@ -441,12 +601,12 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
name="alpha_vantage_api_key"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Alpha Vantage API Key(選填)</FormLabel>
|
||||
<FormLabel>Alpha Vantage API Key *</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="選填,用於更詳細的數據" {...field} />
|
||||
<Input type="password" placeholder="輸入 Alpha Vantage API Key(必填)" {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
用於獲取更詳細的財務數據(可選)
|
||||
用於獲取市場基本面數據(必填)
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -7,9 +7,24 @@ from openai import OpenAI
|
|||
class FinancialSituationMemory:
|
||||
def __init__(self, name, config):
|
||||
self.embedding = "text-embedding-3-small"
|
||||
# Get the OpenAI API key from environment variable
|
||||
openai_api_key = os.getenv("OPENAI_API_KEY")
|
||||
self.client = OpenAI(base_url=config["backend_url"], api_key=openai_api_key)
|
||||
|
||||
# Get embedding configuration from config, with fallbacks
|
||||
embedding_base_url = config.get("embedding_base_url", "https://api.openai.com/v1")
|
||||
embedding_api_key = config.get("embedding_api_key")
|
||||
|
||||
# Fallback chain for API key
|
||||
if not embedding_api_key:
|
||||
# Try environment variable first
|
||||
embedding_api_key = os.getenv("OPENAI_API_KEY")
|
||||
if not embedding_api_key:
|
||||
# Fall back to LLM keys as last resort
|
||||
embedding_api_key = config.get("quick_think_api_key") or config.get("deep_think_api_key")
|
||||
if embedding_api_key:
|
||||
import logging
|
||||
logging.warning("Using LLM API key for embeddings. Consider setting embedding_api_key or OPENAI_API_KEY.")
|
||||
|
||||
# Use configured endpoint for embeddings
|
||||
self.client = OpenAI(base_url=embedding_base_url, api_key=embedding_api_key)
|
||||
self.chroma_client = chromadb.Client(Settings(allow_reset=True))
|
||||
self.situation_collection = self.chroma_client.get_or_create_collection(name=name)
|
||||
|
||||
|
|
|
|||
|
|
@ -81,29 +81,45 @@ class TradingAgentsGraph:
|
|||
exist_ok=True,
|
||||
)
|
||||
|
||||
# 初始化 LLM
|
||||
# 初始化 LLM
|
||||
provider = self.config["llm_provider"].lower()
|
||||
if provider in ["openai"]:
|
||||
# Get the OpenAI API key from environment variable
|
||||
openai_api_key = os.getenv("OPENAI_API_KEY")
|
||||
self.deep_thinking_llm = ChatOpenAI(
|
||||
model=self.config["deep_think_llm"],
|
||||
base_url=self.config["backend_url"],
|
||||
openai_api_key=openai_api_key
|
||||
)
|
||||
self.quick_thinking_llm = ChatOpenAI(
|
||||
model=self.config["quick_think_llm"],
|
||||
base_url=self.config["backend_url"],
|
||||
openai_api_key=openai_api_key
|
||||
)
|
||||
elif provider == "anthropic":
|
||||
self.deep_thinking_llm = ChatAnthropic(model=self.config["deep_think_llm"], base_url=self.config["backend_url"])
|
||||
self.quick_thinking_llm = ChatAnthropic(model=self.config["quick_think_llm"], base_url=self.config["backend_url"])
|
||||
elif provider == "google":
|
||||
self.deep_thinking_llm = ChatGoogleGenerativeAI(model=self.config["deep_think_llm"])
|
||||
self.quick_thinking_llm = ChatGoogleGenerativeAI(model=self.config["quick_think_llm"])
|
||||
else:
|
||||
raise ValueError(f"不支援的 LLM 供應商: {self.config['llm_provider']}")
|
||||
|
||||
# Get base URLs from config, defaulting to backend_url for backward compatibility
|
||||
deep_base_url = self.config.get("deep_think_base_url", self.config.get("backend_url"))
|
||||
quick_base_url = self.config.get("quick_think_base_url", self.config.get("backend_url"))
|
||||
|
||||
# Get API keys from config
|
||||
deep_api_key = self.config.get("deep_think_api_key", os.getenv("OPENAI_API_KEY"))
|
||||
quick_api_key = self.config.get("quick_think_api_key", os.getenv("OPENAI_API_KEY"))
|
||||
|
||||
# Helper to initialize LLM based on URL/Provider
|
||||
def _create_llm(model: str, base_url: str, api_key: str):
|
||||
# Determine provider based on Base URL
|
||||
if "anthropic.com" in base_url:
|
||||
return ChatAnthropic(model=model, base_url=base_url, api_key=api_key)
|
||||
else:
|
||||
# Default to ChatOpenAI for OpenAI, Grok, DeepSeek, Qwen, and other OpenAI-compatible APIs
|
||||
return ChatOpenAI(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
openai_api_key=api_key
|
||||
)
|
||||
|
||||
# Initialize LLMs independently
|
||||
print(f"DEBUG: Initializing Deep Thinking LLM: Model={self.config['deep_think_llm']}, BaseURL={deep_base_url}, Key={deep_api_key[:10]}...")
|
||||
self.deep_thinking_llm = _create_llm(
|
||||
self.config["deep_think_llm"],
|
||||
deep_base_url,
|
||||
deep_api_key
|
||||
)
|
||||
|
||||
print(f"DEBUG: Initializing Quick Thinking LLM: Model={self.config['quick_think_llm']}, BaseURL={quick_base_url}, Key={quick_api_key[:10]}...")
|
||||
self.quick_thinking_llm = _create_llm(
|
||||
self.config["quick_think_llm"],
|
||||
quick_base_url,
|
||||
quick_api_key
|
||||
)
|
||||
|
||||
# 初始化記憶體
|
||||
self.bull_memory = FinancialSituationMemory("bull_memory", self.config)
|
||||
|
|
|
|||
Loading…
Reference in New Issue