Compare commits
2 Commits
483f358972
...
e2f5aef205
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2f5aef205 | ||
|
|
e8ff4b80f8 |
15
Dockerfile
15
Dockerfile
@ -50,6 +50,15 @@ ENV PYTHONPATH=/app
|
|||||||
# 创建数据和日志目录
|
# 创建数据和日志目录
|
||||||
RUN mkdir -p /app/cryptoai/data /app/cryptoai/logs
|
RUN mkdir -p /app/cryptoai/data /app/cryptoai/logs
|
||||||
|
|
||||||
# 设置入口点
|
# 复制API启动脚本
|
||||||
ENTRYPOINT []
|
COPY run_api.py ./
|
||||||
CMD ["python", "run.py"]
|
|
||||||
|
# 暴露API端口
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# 启动命令行(使用CMD,方便docker-compose覆盖)
|
||||||
|
CMD ["python", "run.py"]
|
||||||
|
|
||||||
|
# API服务启动方式:
|
||||||
|
# docker run -p 8000:8000 -e "COMMAND=api" your-image-name
|
||||||
|
# 或者在docker-compose.yml中设置command: python run_api.py
|
||||||
51
README.md
51
README.md
@ -126,4 +126,53 @@ docker run cryptoai --run-once --agent crypto
|
|||||||
|
|
||||||
## 数据库配置
|
## 数据库配置
|
||||||
|
|
||||||
项目使用SQLAlchemy ORM连接MySQL数据库,用于存储分析结果。默认连接到远程MySQL服务器,可以通过环境变量或配置文件自定义连接信息。
|
项目使用SQLAlchemy ORM连接MySQL数据库,用于存储分析结果。默认连接到远程MySQL服务器,可以通过环境变量或配置文件自定义连接信息。
|
||||||
|
|
||||||
|
## API服务
|
||||||
|
|
||||||
|
项目现已集成FastAPI框架,提供了HTTP API接口,方便前端系统调用。
|
||||||
|
|
||||||
|
### 启动API服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 启动API服务
|
||||||
|
python run_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
API服务默认运行在 `http://0.0.0.0:8000`,可以通过环境变量自定义主机和端口:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
API_HOST=127.0.0.1 API_PORT=8080 python run_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### API文档
|
||||||
|
|
||||||
|
启动服务后,可以通过以下URL访问API文档:
|
||||||
|
|
||||||
|
- Swagger UI: `http://localhost:8000/docs`
|
||||||
|
- ReDoc: `http://localhost:8000/redoc`
|
||||||
|
|
||||||
|
### 主要API接口
|
||||||
|
|
||||||
|
- `GET /` - API信息
|
||||||
|
- `GET /health` - 健康检查
|
||||||
|
- `POST /api/analyze` - 分析加密货币市场趋势
|
||||||
|
- `GET /api/token-usage` - 获取DeepSeek API的token使用统计
|
||||||
|
- `POST /api/export-usage` - 导出token使用统计
|
||||||
|
|
||||||
|
### API调用示例
|
||||||
|
|
||||||
|
使用curl调用分析接口:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/api/analyze" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"prompt": "分析比特币最近的市场趋势",
|
||||||
|
"symbol": "BTC",
|
||||||
|
"task_type": "市场分析"
|
||||||
|
}'
|
||||||
|
```
|
||||||
@ -56,10 +56,7 @@ class CryptoAgent:
|
|||||||
test_mode=self.okx_config['test_mode']
|
test_mode=self.okx_config['test_mode']
|
||||||
)
|
)
|
||||||
|
|
||||||
self.deepseek_api = DeepSeekAPI(
|
self.deepseek_api = DeepSeekAPI()
|
||||||
api_key=self.deepseek_config['api_key'],
|
|
||||||
model=self.deepseek_config['model']
|
|
||||||
)
|
|
||||||
|
|
||||||
# 初始化数据处理器
|
# 初始化数据处理器
|
||||||
self.data_processor = DataProcessor(storage_path=self.data_config['storage_path'])
|
self.data_processor = DataProcessor(storage_path=self.data_config['storage_path'])
|
||||||
|
|||||||
@ -52,10 +52,7 @@ class GoldAgent:
|
|||||||
api_key=self.alltick_config['api_key']
|
api_key=self.alltick_config['api_key']
|
||||||
)
|
)
|
||||||
|
|
||||||
self.deepseek_api = DeepSeekAPI(
|
self.deepseek_api = DeepSeekAPI()
|
||||||
api_key=self.deepseek_config['api_key'],
|
|
||||||
model=self.deepseek_config['model']
|
|
||||||
)
|
|
||||||
|
|
||||||
# 初始化数据处理器
|
# 初始化数据处理器
|
||||||
self.data_processor = DataProcessor(storage_path=os.path.join(self.data_config['storage_path'], 'gold'))
|
self.data_processor = DataProcessor(storage_path=os.path.join(self.data_config['storage_path'], 'gold'))
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""API模块,提供与外部API交互的功能。"""
|
"""API模块,提供与外部API交互的功能。"""
|
||||||
|
|
||||||
|
# from cryptoai.api.deepseek_api import DeepSeekAPI
|
||||||
|
# from cryptoai.api.binance_api import BinanceAPI
|
||||||
|
# from cryptoai.api.okx_api import OKXAPI
|
||||||
|
# from cryptoai.api.alltick_api import AlltickAPI
|
||||||
Binary file not shown.
Binary file not shown.
@ -6,6 +6,8 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from cryptoai.utils.config_loader import ConfigLoader
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
@ -19,7 +21,7 @@ logging.basicConfig(
|
|||||||
class DeepSeekAPI:
|
class DeepSeekAPI:
|
||||||
"""DeepSeek API交互类,用于进行大语言模型调用"""
|
"""DeepSeek API交互类,用于进行大语言模型调用"""
|
||||||
|
|
||||||
def __init__(self, api_key: str, model: str = "deepseek-moe-16b-chat"):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
初始化DeepSeek API
|
初始化DeepSeek API
|
||||||
|
|
||||||
@ -27,12 +29,16 @@ class DeepSeekAPI:
|
|||||||
api_key: DeepSeek API密钥
|
api_key: DeepSeek API密钥
|
||||||
model: 使用的模型名称
|
model: 使用的模型名称
|
||||||
"""
|
"""
|
||||||
self.api_key = api_key
|
|
||||||
self.model = model
|
config_loader = ConfigLoader()
|
||||||
|
self.deepseek_config = config_loader.get_deepseek_config()
|
||||||
|
|
||||||
|
self.api_key = self.deepseek_config['api_key']
|
||||||
|
self.model = self.deepseek_config['model']
|
||||||
self.base_url = "https://api.deepseek.com/v1"
|
self.base_url = "https://api.deepseek.com/v1"
|
||||||
self.headers = {
|
self.headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Authorization": f"Bearer {api_key}"
|
"Authorization": f"Bearer {self.api_key}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Token 使用统计
|
# Token 使用统计
|
||||||
@ -45,6 +51,58 @@ class DeepSeekAPI:
|
|||||||
|
|
||||||
# 创建日志记录器
|
# 创建日志记录器
|
||||||
self.logger = logging.getLogger("DeepSeekAPI")
|
self.logger = logging.getLogger("DeepSeekAPI")
|
||||||
|
|
||||||
|
def streaming_call(self, user_prompt: str):
|
||||||
|
"""
|
||||||
|
流式调用DeepSeek API
|
||||||
|
"""
|
||||||
|
|
||||||
|
system_prompt = "你是一个专业的区块链分析高手"
|
||||||
|
|
||||||
|
try:
|
||||||
|
endpoint = f"{self.base_url}/chat/completions"
|
||||||
|
payload = {
|
||||||
|
"model": self.model,
|
||||||
|
"messages": [{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}],
|
||||||
|
"stream": True
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(endpoint, headers=self.headers, json=payload, stream=True)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
for line in response.iter_lines():
|
||||||
|
if line:
|
||||||
|
# 解码二进制数据为字符串
|
||||||
|
line = line.decode('utf-8')
|
||||||
|
|
||||||
|
# 跳过空行和心跳检查行
|
||||||
|
if not line or line == "data: [DONE]":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 移除 "data: " 前缀
|
||||||
|
if line.startswith("data: "):
|
||||||
|
line = line[6:]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 解析JSON数据
|
||||||
|
data = json.loads(line)
|
||||||
|
|
||||||
|
# 提取content内容
|
||||||
|
if (data.get("choices") and
|
||||||
|
len(data["choices"]) > 0 and
|
||||||
|
data["choices"][0].get("delta") and
|
||||||
|
data["choices"][0]["delta"].get("content")):
|
||||||
|
|
||||||
|
content = data["choices"][0]["delta"]["content"]
|
||||||
|
yield content
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
self.logger.error(f"解析JSON时出错: {e}, 原始数据: {line}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"流式调用DeepSeek API时出错: {e}")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def call_model(self, prompt: str, system_prompt: str = None, task_type: str = "未知任务", symbol: str = "未知", temperature: float = 0.2, max_tokens: int = 2000) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
def call_model(self, prompt: str, system_prompt: str = None, task_type: str = "未知任务", symbol: str = "未知", temperature: float = 0.2, max_tokens: int = 2000) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
34
cryptoai/routes/agent.py
Normal file
34
cryptoai/routes/agent.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
API路由模块,为前端提供REST API接口
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status, Body
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from cryptoai.api.deepseek_api import DeepSeekAPI
|
||||||
|
from cryptoai.utils.config_loader import ConfigLoader
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
# 创建路由
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
class ChatRequest(BaseModel):
|
||||||
|
user_prompt: str
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/chat")
|
||||||
|
async def chat(request: ChatRequest):
|
||||||
|
"""
|
||||||
|
聊天接口
|
||||||
|
"""
|
||||||
|
|
||||||
|
deepseek_api = DeepSeekAPI()
|
||||||
|
response = deepseek_api.streaming_call(request.user_prompt)
|
||||||
|
|
||||||
|
return StreamingResponse(response, media_type="text/plain")
|
||||||
110
cryptoai/routes/fastapi_app.py
Normal file
110
cryptoai/routes/fastapi_app.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
FastAPI应用程序入口
|
||||||
|
为CryptoAI系统提供web API接口层
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import uvicorn
|
||||||
|
from fastapi import FastAPI, Request
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from cryptoai.routes.agent import router as agent_router
|
||||||
|
|
||||||
|
# 配置日志
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler("api_server.log"),
|
||||||
|
logging.StreamHandler()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
logger = logging.getLogger("fastapi")
|
||||||
|
|
||||||
|
# 创建FastAPI应用
|
||||||
|
app = FastAPI(
|
||||||
|
title="CryptoAI API",
|
||||||
|
description="加密货币AI分析系统API接口",
|
||||||
|
version="0.1.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加CORS中间件
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"], # 可以设置为特定域名,如["http://localhost:3000"]
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加API路由
|
||||||
|
app.include_router(agent_router, prefix="/agent")
|
||||||
|
|
||||||
|
# 请求计时中间件
|
||||||
|
@app.middleware("http")
|
||||||
|
async def add_process_time_header(request: Request, call_next):
|
||||||
|
start_time = time.time()
|
||||||
|
response = await call_next(request)
|
||||||
|
process_time = time.time() - start_time
|
||||||
|
response.headers["X-Process-Time"] = str(process_time)
|
||||||
|
return response
|
||||||
|
|
||||||
|
# 根路由
|
||||||
|
@app.get("/", tags=["信息"])
|
||||||
|
async def root() -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
API根路径,提供API基本信息
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"name": "CryptoAI API",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "加密货币AI分析系统API接口",
|
||||||
|
"documentation": "/docs",
|
||||||
|
"status": "running"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
@app.get("/health", tags=["信息"])
|
||||||
|
async def health_check() -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
API健康检查接口
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"status": "healthy",
|
||||||
|
"timestamp": time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
# 异常处理
|
||||||
|
@app.exception_handler(Exception)
|
||||||
|
async def global_exception_handler(request: Request, exc: Exception):
|
||||||
|
logger.error(f"全局异常: {str(exc)}", exc_info=True)
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=500,
|
||||||
|
content={"detail": f"服务器内部错误: {str(exc)}"}
|
||||||
|
)
|
||||||
|
|
||||||
|
def start():
|
||||||
|
"""
|
||||||
|
启动FastAPI服务器
|
||||||
|
"""
|
||||||
|
# 获取环境变量或使用默认值
|
||||||
|
host = os.environ.get("API_HOST", "127.0.0.1")
|
||||||
|
port = int(os.environ.get("API_PORT", 8000))
|
||||||
|
|
||||||
|
# 启动服务器
|
||||||
|
uvicorn.run(
|
||||||
|
"cryptoai.routes.fastapi_app:app",
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
reload=True # 生产环境设为False
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
start()
|
||||||
@ -24,17 +24,7 @@ def get_deepseek_api() -> DeepSeekAPI:
|
|||||||
"""
|
"""
|
||||||
获取已配置的DeepSeekAPI实例
|
获取已配置的DeepSeekAPI实例
|
||||||
"""
|
"""
|
||||||
config_loader = ConfigLoader()
|
return DeepSeekAPI()
|
||||||
deepseek_config = config_loader.get_deepseek_config()
|
|
||||||
|
|
||||||
if not deepseek_config or 'api_key' not in deepseek_config:
|
|
||||||
print("错误: 未找到DeepSeek API配置或API密钥")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
return DeepSeekAPI(
|
|
||||||
api_key=deepseek_config['api_key'],
|
|
||||||
model=deepseek_config.get('model', 'deepseek-moe-16b-chat')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def show_token_usage_stats():
|
def show_token_usage_stats():
|
||||||
|
|||||||
@ -2,20 +2,37 @@ version: '3.8'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
cryptoai:
|
cryptoai:
|
||||||
build:
|
build: .
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: cryptoai
|
container_name: cryptoai
|
||||||
image: cryptoai:0.0.6
|
image: cryptoai:0.0.7
|
||||||
restart: unless-stopped
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
# 挂载配置文件
|
- ./cryptoai/data:/app/cryptoai/data
|
||||||
- ./cryptoai/config/config.yaml:/app/cryptoai/config/config.yaml
|
- ./cryptoai/logs:/app/cryptoai/logs
|
||||||
# 持久化数据和日志
|
environment:
|
||||||
- cryptoai_data:/app/cryptoai/data
|
- TZ=Asia/Shanghai
|
||||||
- cryptoai_logs:/app/logs
|
|
||||||
command: python run.py
|
command: python run.py
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
|
||||||
volumes:
|
cryptoai-api:
|
||||||
cryptoai_data:
|
build: .
|
||||||
cryptoai_logs:
|
container_name: cryptoai-api
|
||||||
|
image: cryptoai-api:0.0.1
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
volumes:
|
||||||
|
- ./cryptoai/data:/app/cryptoai/data
|
||||||
|
- ./cryptoai/logs:/app/cryptoai/logs
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- API_HOST=0.0.0.0
|
||||||
|
- API_PORT=8000
|
||||||
|
command: python run_api.py
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-network:
|
||||||
|
driver: bridge
|
||||||
@ -7,6 +7,9 @@ requests==2.31.0
|
|||||||
schedule==1.2.0
|
schedule==1.2.0
|
||||||
scikit-learn==1.3.2
|
scikit-learn==1.3.2
|
||||||
pyyaml==6.0.1
|
pyyaml==6.0.1
|
||||||
|
fastapi==0.110.0
|
||||||
|
uvicorn==0.27.1
|
||||||
|
python-dotenv==1.0.0
|
||||||
# # 日志相关
|
# # 日志相关
|
||||||
# logging==0.4.9.6
|
# logging==0.4.9.6
|
||||||
# # 数据处理相关
|
# # 数据处理相关
|
||||||
|
|||||||
19
run_api.py
Normal file
19
run_api.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
CryptoAI API 服务启动脚本
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# 添加项目根目录到Python路径
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
sys.path.append(current_dir)
|
||||||
|
|
||||||
|
from cryptoai.routes.fastapi_app import start
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("启动 CryptoAI API 服务...")
|
||||||
|
start()
|
||||||
Loading…
Reference in New Issue
Block a user