Fix router failover and add Discord bot

- Remove duplicate claude_api provider to fix automatic failover
- Enhance error detection with HTTP status codes and more indicators
- Add comprehensive README documentation with manual switching
- Implement Discord bot with Claude Code CLI integration
- Support /terminal and /claude commands with AI-powered responses

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Will Song
2025-07-16 21:24:06 -05:00
parent 13e98d1229
commit 568dcc45e4
29 changed files with 2732 additions and 204 deletions

View File

@@ -1,238 +1,314 @@
# Claude Router
# Claude API Router
一个智能的Claude API路由器支持Claude Pro和Claude API之间的自动故障转移。当Claude Pro达到使用限制时自动切换到Claude API确保服务的连续性
智能的Claude API路由器提供自动故障转移和负载均衡功能。当主要API服务遇到限制或错误时自动切换到备用提供商
## 功能特性
## 🚀 功能特性
- **自动故障转移**: 检测到速率限制或使用限制时自动切换provider
- **定时健康检查**: 每小时前5分钟自动检测Claude Pro限额恢复
- **智能恢复**: 自动切换回Claude Pro优先使用高级功能
- **手动切换**: 支持手动切换到指定provider
- **兼容Claude Code CLI**: 完全兼容Anthropic API格式
- **Docker化部署**: 一键部署,开箱即用
- **自动故障转移**: 检测到速率限制或错误时自动切换API提供商
- **多提供商支持**: 支持Claude Pro、DeepSeek、Kimi
- **手动切换**: 支持手动强制切换到指定提供商
- **健康检查**: 自动监控和恢复主要提供商
- **完全兼容**: 与Anthropic Claude API完全兼容
- **流式响应**: 支持流式和非流式响应
## 快速开始
## 📋 支持的提供商
### 1. 使用Docker Compose部署
按优先级排序:
1. **Claude Pro** - 最高优先级,首选提供商
2. **DeepSeek** - OpenAI兼容的备用提供商
3. **Kimi** - OpenAI兼容的备用提供商
## 🛠️ 安装和配置
### 1. 环境要求
```bash
# 克隆或进入项目目录
cd /home/will/docker/router
cd router
pip install -r requirements.txt
```
# 构建并启动服务
### 2. 配置API密钥
`/home/will/docker/tokens.txt` 文件中添加以下令牌:
```txt
claude_api_key=your_claude_api_key_here
deepseek_api_key=your_deepseek_api_key_here
kimi_api_key=your_kimi_api_key_here
```
或者设置环境变量:
```bash
export CLAUDE_API_KEY="your_claude_api_key"
export DEEPSEEK_API_KEY="your_deepseek_api_key"
export KIMI_API_KEY="your_kimi_api_key"
```
### 3. 启动服务
```bash
# 开发模式
python app.py
# 生产模式 (使用Docker Compose)
docker-compose up -d
# 查看服务状态
docker-compose ps
```
### 2. 验证服务运行
服务将在 `http://localhost:8000` 启动。
```bash
# 健康检查
curl http://localhost:8000/health
## 🔧 手动切换提供商
# 查看当前状态
curl http://localhost:8000/v1/status
```
### API端点
### 3. 配置Claude Code CLI
```http
POST /v1/switch-provider
Content-Type: application/json
设置环境变量将Claude Code CLI指向路由器
```bash
# 设置API endpoint为路由器地址
export ANTHROPIC_API_URL="http://localhost:8000"
# 添加到bashrc使其永久生效
echo 'export ANTHROPIC_API_URL="http://localhost:8000"' >> ~/.bashrc
# 测试配置
echo "Hello Claude Router" | claude --print
```
**注意**: 无需修改ANTHROPIC_API_KEY路由器会自动处理API密钥。
## API端点
### 主要端点
- `POST /v1/messages` - Claude API消息创建兼容Anthropic API
- `GET /health` - 健康检查
- `GET /v1/status` - 获取路由器状态
- `POST /v1/switch-provider` - 手动切换provider
- `POST /v1/health-check` - 手动触发Claude Pro健康检查
### 健康检查响应示例
```json
{
"status": "healthy",
"current_provider": "claude_pro",
"failover_count": 0,
"last_failover": null,
"last_health_check": "2025-07-14T19:00:00.000Z",
"health_check_failures": 0,
"providers": {
"claude_pro": {"active": true},
"claude_api": {"active": true}
}
"provider": "provider_name"
}
```
## 配置说明
### 可用的提供商名称
### 环境变量
- `claude_pro` - Claude Pro提供商 (默认)
- `deepseek` - DeepSeek提供商
- `kimi` - Kimi提供商
- `CLAUDE_API_KEY`: Claude API密钥
- `ROUTER_HOST`: 服务监听地址(默认: 0.0.0.0
- `ROUTER_PORT`: 服务监听端口(默认: 8000
- `MAX_RETRIES`: 最大重试次数(默认: 3
- `RETRY_DELAY`: 重试延迟(默认: 1.0秒)
### 使用示例
### 健康检查配置
- `health_check_enabled`: 是否启用定时健康检查(默认: true
- `health_check_cron`: 检查时间表达式(默认: "0-4 * * * *" - 每小时前5分钟
- `health_check_message`: 测试消息内容(默认: "ping"
- `health_check_model`: 使用的模型(默认: claude-3-haiku-20240307
### Token文件
路由器会自动从 `/home/will/docker/tokens.txt` 读取API密钥无需手动配置环境变量。
## 故障转移机制
当检测到以下错误时路由器会自动切换到下一个可用的provider
- 429 (Too Many Requests)
- 速率限制错误
- 使用限制达到
- "usage limit reached"相关错误
**优先级顺序**: Claude Pro → Claude API
## 使用示例
### 基本API调用
```bash
curl -X POST http://localhost:8000/v1/messages \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_api_key" \
-d '{
"model": "claude-3-sonnet-20240229",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hello, Claude!"}
]
}'
```
### 手动切换provider
#### curl命令
```bash
# 切换到DeepSeek
curl -X POST http://localhost:8000/v1/switch-provider \
-H "Content-Type: application/json" \
-d '"claude_api"'
-d '{"provider": "deepseek"}'
# 切换到Kimi
curl -X POST http://localhost:8000/v1/switch-provider \
-H "Content-Type: application/json" \
-d '{"provider": "kimi"}'
# 切换回Claude Pro
curl -X POST http://localhost:8000/v1/switch-provider \
-H "Content-Type: application/json" \
-d '{"provider": "claude_pro"}'
```
### 手动健康检查
#### Python脚本
```python
import requests
def switch_provider(provider_name):
"""切换API提供商"""
url = "http://localhost:8000/v1/switch-provider"
data = {"provider": provider_name}
response = requests.post(url, json=data)
if response.status_code == 200:
result = response.json()
print(f"成功切换到: {result['current_provider']}")
return True
else:
print(f"切换失败: {response.text}")
return False
# 使用示例
switch_provider("deepseek")
switch_provider("kimi")
switch_provider("claude_pro")
```
### 查看当前状态
```bash
# 立即检测Claude Pro是否可用
curl -X POST http://localhost:8000/v1/health-check
# 查看当前使用的提供商
curl http://localhost:8000/v1/health
# 查看详细状态
curl http://localhost:8000/v1/status
# 响应示例
{
"status": "healthy",
"current_provider": "claude_pro",
"providers": {
"claude_pro": "available",
"deepseek": "unknown",
"kimi": "unknown"
}
}
```
## 开发和调试
## 🔄 自动故障转移
### 本地开发
路由器会自动检测以下错误并进行故障转移:
```bash
# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate
- 速率限制 (Rate limit exceeded)
- 使用配额超额 (Usage limit exceeded)
- HTTP 429错误
- 每日/月度限制达到
- 网络连接错误
# 安装依赖
pip install -r requirements.txt
### 故障转移流程
# 运行应用
python app.py
1. 检测到故障转移触发条件
2. 自动切换到下一个优先级提供商
3. 重试请求
4. 记录切换日志
5. 定期尝试恢复到主要提供商
## 🩺 健康检查和自动恢复
- **自动检查频率**: 每小时的前5分钟
- **检查内容**: 向Claude Pro发送测试请求
- **自动恢复**: 如果Claude Pro恢复可用自动切换回去
## 🔌 API兼容性
路由器完全兼容Anthropic Claude API支持
- 所有Claude模型 (claude-3-sonnet, claude-3-opus等)
- 流式和非流式响应
- 系统消息和用户消息
- 所有API参数 (max_tokens, temperature等)
### 使用示例
```python
import requests
# 标准Claude API调用
url = "http://localhost:8000/v1/messages"
headers = {
"Content-Type": "application/json",
"x-api-key": "your_claude_api_key"
}
data = {
"model": "claude-3-sonnet-20240229",
"max_tokens": 1000,
"messages": [
{"role": "user", "content": "Hello, Claude!"}
]
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
```
### 查看日志
```bash
# Docker容器日志
docker-compose logs -f claude-router
# 实时日志
docker logs -f claude-router
```
## 故障排除
## 🐛 故障排除
### 常见问题
1. **服务无法启动**
- 检查tokens.txt文件是否存在且格式正确
- 确认端口8000未被占用
#### 1. 自动切换不工作
2. **API调用失败**
- 验证API密钥是否有效
- 检查网络连接到api.anthropic.com
**问题**: 遇到限制时不自动切换
3. **自动切换不工作**
- 查看日志确认错误检测逻辑
- 确认backup provider配置正确
**解决方案**:
```bash
# 手动切换到备用提供商
curl -X POST http://localhost:8000/v1/switch-provider \
-H "Content-Type: application/json" \
-d '{"provider": "deepseek"}'
```
### 监控
#### 2. API密钥错误
- 健康检查: `http://localhost:8000/health`
- 状态监控: `http://localhost:8000/v1/status`
- Docker健康检查: `docker inspect claude-router`
**问题**: 提示API密钥无效
## 技术架构
**解决方案**:
1. 检查 `tokens.txt` 文件中的密钥格式
2. 确保密钥没有多余的空格或换行
3. 验证密钥是否有效
- **框架**: FastAPI + Uvicorn
- **HTTP客户端**: httpx
- **AI库**: anthropic
- **容器化**: Docker + Docker Compose
- **配置管理**: pydantic + python-dotenv
#### 3. 服务无法启动
## 版本信息
**问题**: 路由器启动失败
- 版本: 1.0.0 (MVP)
- Python: 3.11+
- 支持: Claude-3 系列模型
**解决方案**:
```bash
# 检查日志
docker-compose logs router
## 更新日志
# 手动启动调试
cd router
python app.py
```
### v1.1.0 (2025-07-14)
- ✅ 添加定时健康检查功能
- ✅ 每小时前5分钟自动检测Claude Pro限额恢复
- ✅ 智能自动切换回Claude Pro
- ✅ 新增手动健康检查API
- ✅ 完善日志记录和状态监控
### 调试模式
### v1.0.0 (2025-07-14)
- ✅ 基础路由器功能
- ✅ Claude Pro到Claude API自动故障转移
- ✅ Docker容器化部署
- ✅ Claude Code CLI兼容性
启用详细日志:
## 后续开发计划
```python
import logging
logging.basicConfig(level=logging.DEBUG)
```
- [ ] 添加DeepSeek API支持
- [ ] 添加ChatGPT API支持
- [ ] 实现请求统计和监控面板
- [ ] 添加请求缓存功能
- [ ] 支持负载均衡
- [ ] 集成Kimi v2 API
## 📊 监控和日志
## 许可证
### 日志级别
MIT License
- `INFO`: 正常操作日志
- `WARNING`: 提供商切换警告
- `ERROR`: 错误和故障转移事件
### 关键日志信息
- 提供商切换事件
- API请求失败
- 健康检查结果
- 自动恢复操作
## ⚙️ 高级配置
### 自定义故障转移条件
编辑 `app.py` 中的 `should_failover` 方法:
```python
def should_failover(self, error_message: str) -> bool:
# 添加自定义错误检测条件
custom_indicators = [
"your_custom_error",
"specific_provider_error"
]
return any(indicator in error_message.lower()
for indicator in custom_indicators)
```
### 调整健康检查频率
修改 `app.py` 中的定时任务:
```python
# 每30分钟检查一次
@scheduler.cron("*/30 * * * *")
async def health_check_claude_pro():
# ... 健康检查逻辑
```
## 🤝 SillyTavern集成
在SillyTavern中使用此路由器
1. **API URL**: `http://localhost:8000/v1/messages`
2. **API Key**: 使用你的Claude API密钥
3. **模型**: 选择任何Claude模型
路由器会自动处理故障转移对SillyTavern完全透明。
## 📄 许可证
MIT License
## 🆘 获取帮助
如果遇到问题:
1. 检查日志文件
2. 验证API密钥配置
3. 尝试手动切换提供商
4. 查看健康检查状态
需要进一步帮助请查看代码注释或联系开发者。

View File

@@ -26,11 +26,11 @@ class ClaudeRouter:
self.last_health_check = None
self.health_check_failures = 0
self.scheduler = None
# 按优先级顺序排列Claude Pro > DeepSeek > Kimi > Claude API
# 按优先级顺序排列Claude Pro > DeepSeek > Kimi (removed claude_api)
from collections import OrderedDict
self.providers = OrderedDict([
("claude_pro", {
"api_key": config.claude_pro_api_key,
"api_key": config.claude_api_key, # Use claude_api_key for claude_pro
"base_url": config.claude_pro_base_url,
"type": "anthropic",
"active": True
@@ -46,12 +46,6 @@ class ClaudeRouter:
"base_url": config.kimi_base_url,
"type": "openai",
"active": True
}),
("claude_api", {
"api_key": config.claude_api_key,
"base_url": config.claude_api_base_url,
"type": "anthropic",
"active": True
})
])
@@ -220,19 +214,21 @@ class ClaudeRouter:
return response
async def health_check_claude_pro(self):
"""Check if Claude Pro is available again"""
# Only check if we're not currently using Claude Pro
if self.current_provider == "claude_pro":
logger.debug("Skipping health check - already using Claude Pro")
async def health_check_primary_provider(self):
"""Check if primary provider (claude_pro) is available again"""
primary_provider = "claude_pro"
# Only check if we're not currently using the primary provider
if self.current_provider == primary_provider:
logger.debug(f"Skipping health check - already using {primary_provider}")
return
logger.info("Running Claude Pro health check...")
logger.info(f"Running {primary_provider} health check...")
self.last_health_check = datetime.now()
try:
client = Anthropic(
api_key=config.claude_pro_api_key,
api_key=config.claude_api_key, # Use claude_api_key for claude_pro
base_url=config.claude_pro_base_url
)
@@ -253,21 +249,21 @@ class ClaudeRouter:
prompt=f"Human: {config.health_check_message}\n\nAssistant:"
)
# If successful, switch back to Claude Pro
# If successful, switch back to primary provider
old_provider = self.current_provider
self.current_provider = "claude_pro"
self.current_provider = primary_provider
self.health_check_failures = 0
logger.info(f"Claude Pro health check successful! Switched from {old_provider} to claude_pro")
logger.info(f"{primary_provider} health check successful! Switched from {old_provider} to {primary_provider}")
except Exception as e:
self.health_check_failures += 1
error_str = str(e).lower()
if any(indicator in error_str for indicator in ["rate_limit", "usage limit", "quota exceeded", "429", "too many requests", "limit reached"]):
logger.info(f"Claude Pro still rate limited: {str(e)}")
logger.info(f"{primary_provider} still rate limited: {str(e)}")
else:
logger.warning(f"Claude Pro health check failed (attempt {self.health_check_failures}): {str(e)}")
logger.warning(f"{primary_provider} health check failed (attempt {self.health_check_failures}): {str(e)}")
def start_scheduler(self):
"""Start the health check scheduler"""

View File

@@ -18,7 +18,7 @@ class Config(BaseModel):
retry_delay: float = 1.0
# API endpoints
claude_pro_base_url: str = "https://api.anthropic.com"
claude_pro_base_url: str = "https://api.anthropic.com" # Claude Pro might use different endpoint in future
claude_api_base_url: str = "https://api.anthropic.com"
deepseek_base_url: str = "https://api.deepseek.com"
kimi_base_url: str = "https://api.moonshot.ai"
@@ -57,8 +57,8 @@ class Config(BaseModel):
except FileNotFoundError:
pass
# For MVP, we'll use the same API key for both pro and regular
# In practice, Claude Pro might use a different endpoint or key
# Claude Pro uses the same API key as regular Claude API
# but may use different endpoint or have different rate limits
self.claude_pro_api_key = self.claude_api_key
# Global config instance