Commit Graph

103 Commits

Author SHA1 Message Date
SunSeekerX
029bdf3719 1 2026-01-21 11:55:28 +08:00
root
189d53d793 style: fix ESLint prefer-const and formatting
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:46:08 +00:00
root
b148537428 style: fix prettier formatting
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:42:39 +00:00
root
b26027731e fix(memory): clear bodyString after req.write() to prevent closure capture
Additional memory optimizations:
- Set bodyString = null after req.write() in both stream and non-stream requests
- Use let instead of const for bodyString to allow nullifying
- Store non-stream originalBodyString in bodyStore to avoid closure capture
- Clean up bodyStore in finally block for non-stream requests

This prevents V8 closures (res.on handlers) from retaining large request
body strings until stream completion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 08:57:54 +00:00
root
f535b35a1c fix(memory): use bodyStore to avoid closure capturing request body
Problem:
- Stream response handlers (res.on) captured requestOptions in closures
- requestOptions contained originalBodyString (~800KB per request)
- These strings couldn't be GC'd until stream completed
- With concurrent requests, memory accumulated rapidly

Solution:
- Store request body strings in this.bodyStore Map with unique ID
- Pass only bodyStoreId in requestOptions (not the 800KB string)
- Closures capture small ID, not large string
- Clean up bodyStore on request completion (success/error/timeout)
- Extract needed values before closures to avoid capturing body object
2026-01-12 08:31:47 +00:00
jett
b397954ea4 fix lint 2026-01-10 11:41:35 +08:00
jett
ed835d0c28 using TTL-based random suffix 2026-01-10 00:56:21 +08:00
jett
28b27e6a7b fix: claude subscription detection 2026-01-10 00:31:17 +08:00
SunSeekerX
f5e982632d chore 2026-01-04 12:05:53 +08:00
SunSeekerX
90023d1551 Revert: 撤销 584fa8c 之后的所有提交 2026-01-03 23:24:59 +08:00
SunSeekerX
b4233033a6 chore 2025-12-31 02:17:10 +08:00
SunSeekerX
584fa8c9c1 feat: 大规模性能优化 - Redis Pipeline 批量操作、索引系统、连接池优化 2025-12-31 02:08:47 +08:00
shaw
67c20fa30e feat: 为 claude-official 账户添加 403 错误重试机制
针对 OAuth 和 Setup Token 类型的 Claude 账户,遇到 403 错误时:
- 休息 2 秒后进行重试
- 最多重试 2 次(总共最多 3 次请求)
- 重试后仍是 403 才标记账户为 blocked

同时支持流式和非流式请求,并修复了流式请求中的竞态条件问题。
2025-12-24 19:54:25 +08:00
shaw
638d2ff189 feat: 支持claude单账户开启串行队列 2025-12-19 22:29:57 +08:00
guoyongchang
09cf951cdc [feat/cron-test-support]done. 2025-12-19 10:25:43 +08:00
DaydreamCoding
07633ddbf8 feat: enhance concurrency queue with health check and admin endpoints
- Add queue health check for fast-fail when overloaded (P90 > threshold)
  - Implement socket identity verification with UUID token
  - Add wait time statistics (P50/P90/P99) and queue stats tracking
  - Add admin endpoints for queue stats and cleanup
  - Add CLEAR_CONCURRENCY_QUEUES_ON_STARTUP config option
  - Update documentation with troubleshooting and proxy config guide
2025-12-12 14:32:09 +08:00
Wesley Liddick
4337af06d4 Merge pull request #791 from DaydreamCoding/feature/log-opt
fix: improve logging for client disconnections in relay services
2025-12-10 07:11:24 -05:00
QTom
8901994644 fix: improve logging for client disconnections in relay services
当客户端主动断开连接时,改为使用 INFO 级别记录而不是 ERROR 级别,
因为这是正常情况而非错误。

- ccrRelayService: 区分客户端断开与实际错误
- claudeConsoleRelayService: 区分客户端断开与实际错误
- claudeRelayService: 区分客户端断开与实际错误
- droidRelayService: 区分客户端断开与实际错误
2025-12-10 14:18:44 +08:00
QTom
3b9c96dff8 feat(queue): 优化用户消息队列锁释放时机
将队列锁释放时机从"请求完成后"提前到"请求发送后",因为 Claude API
限流(RPM)基于请求发送时刻计算,无需等待响应完成。

主要变更:
- 移除锁续租机制(startLockRenewal、refreshUserMessageLock)
- 所有 relay 服务在请求发送成功后立即释放锁
- 流式请求通过 onResponseStart 回调在收到响应头时释放
- 调整默认配置:timeoutMs 60s→5s,lockTtlMs 120s→5s
- 新增 USER_MESSAGE_QUEUE_LOCK_TTL_MS 环境变量支持
2025-12-10 01:26:00 +08:00
QTom
f5d1c25295 feat: 添加用户消息串行队列功能,防止同账户并发请求触发限流
- 新增 userMessageQueueService.js 实现基于 Redis 的队列锁机制
- 在 claudeRelayService、claudeConsoleRelayService、bedrockRelayService、ccrRelayService 中集成队列锁
- 添加 Redis 原子性 Lua 脚本:acquireUserMessageLock、releaseUserMessageLock、refreshUserMessageLock
- 支持锁续租机制,防止长时间请求锁过期
- 添加可配置参数:USER_MESSAGE_QUEUE_ENABLED、USER_MESSAGE_QUEUE_DELAY_MS、USER_MESSAGE_QUEUE_TIMEOUT_MS
- 添加 Web 管理界面配置入口
- 添加 logger.performance 方法用于结构化性能日志
- 添加完整单元测试 (tests/userMessageQueue.test.js)
2025-12-09 17:04:01 +08:00
shaw
659072075d fix: 统一格式化claude参数传递 2025-12-08 14:23:13 +08:00
IanShaw027
0d64d40654 feat: 添加上游不稳定错误检测与账户临时不可用机制
## 背景
当上游 API(如 Anthropic、AWS Bedrock 等)出现临时故障时,服务会持续向故障
账户发送请求,导致用户体验下降。需要自动检测上游不稳定状态并临时排除故障账户。

## 改动内容

### 新增 unstableUpstreamHelper.js
- 检测多种上游不稳定错误模式
- 支持环境变量扩展检测规则

### 修改 unifiedClaudeScheduler.js
- 新增 markAccountTemporarilyUnavailable() 方法:标记账户临时不可用
- 新增 isAccountTemporarilyUnavailable() 方法:检查账户是否临时不可用
- 专属账户检查:claude-official、claude-console、bedrock 临时不可用时自动回退到池
- 池账户选择:跳过临时不可用的账户

### 修改 claudeRelayService.js
- _handleServerError() 方法增加临时不可用标记逻辑
- 5xx 错误时自动标记账户临时不可用(5分钟 TTL)

## 检测的状态码

| 分类 | 状态码 | 说明 |
|------|--------|------|
| 服务器错误 | 500-599 | 内部错误、服务不可用等 |
| 超时类 | 408 | 请求超时 |
| 连接类 | 499 | 客户端关闭请求 (Nginx) |
| 网关类 | 502, 503, 504 | 网关错误、服务不可用、网关超时 |
| CDN类 | 522 | Cloudflare 连接超时 |
| 语义类 | error.type = "server_error" | API 级别服务器错误 |

## 环境变量配置

- UNSTABLE_ERROR_TYPES: 额外的错误类型(逗号分隔)
- UNSTABLE_ERROR_KEYWORDS: 错误消息关键词(逗号分隔)

## Redis 键

- temp_unavailable:{accountType}:{accountId} - TTL 300秒
2025-12-05 02:28:30 +08:00
IanShaw027
5fd78b6411 fix: 过滤 Cloudflare CDN headers 以防止 API 安全检查
使用 Cloudflare 橙色云(CDN 代理模式)时,Cloudflare 会自动添加 CDN 相关的 headers
(cf-*, x-forwarded-*, cdn-loop 等),这会触发上游 API 提供商的安全检查:

1. 已确认问题:88code API 检测到 CDN headers 后返回 403 Forbidden,
   导致 Codex CLI 无法使用
2. 潜在风险:其他 API 提供商(OpenAI、Anthropic)可能也会因检测到
   代理/CDN 特征而采取限制措施

创建统一的 headerFilter 工具类,在所有转发服务中过滤 Cloudflare CDN headers,
使转发请求伪装成正常的直接客户端请求。

1. 新增 src/utils/headerFilter.js
   - 统一的 CDN headers 过滤列表(13 个 Cloudflare headers)
   - 提供 filterForOpenAI() 和 filterForClaude() 方法
   - 在现有过滤逻辑基础上添加 CDN header 过滤

2. 更新 src/services/openaiResponsesRelayService.js
   - 使用 filterForOpenAI() 替代内联的 _filterRequestHeaders()
   - 保持向后兼容性

3. 更新 src/services/claudeRelayService.js
   - 使用 filterForClaude() 替代 _filterClientHeaders() 实现
   - 简化代码,移除重复的 header 列表定义

4. 修复 src/routes/openaiRoutes.js
   - 添加对 input 字段的类型检查(可以是数组或字符串)
   - 防止 "startsWith is not a function" 错误

x-real-ip, x-forwarded-for, x-forwarded-proto, x-forwarded-host,
x-forwarded-port, x-accel-buffering, cf-ray, cf-connecting-ip,
cf-ipcountry, cf-visitor, cf-request-id, cdn-loop, true-client-ip

-  Codex CLI 通过中转服务成功调用 88code API(之前返回 403)
-  保留所有业务必需的 headers(conversation_id、session_id 等)
-  移除所有 Cloudflare CDN 痕迹
-  保持橙色云的 DDoS 防护和 CDN 加速优势
-  Docker 构建成功

1. 解决 88code 403 问题,Codex CLI 可正常使用
2. 降低因 CDN/代理特征被上游 API 识别的风险
3. 提升与各种 API 提供商的兼容性
4. 统一管理 CDN headers 过滤逻辑,便于维护
2025-12-03 07:07:12 -08:00
shaw
e8e6f972b4 fix: 增强console账号test端点 2025-12-01 15:08:40 +08:00
Dave
249e256360 fix: 修复 Claude API 400 错误:tool_result/tool_use 不匹配问题
错误信息:
     messages.14.content.0: unexpected tool_use_id found in tool_result blocks: toolu_01Ekn6YJMk7yt7hNcn4PZxtM.
     Each tool_result block must have a corresponding tool_use block in the previous message.
根本原因:
     文件: src/services/claudeRelayService.js 中的 _enforceCacheControlLimit() 方法
原实现问题:
     1. 当 cache_control 块超过 4 个时,直接删除整个内容块(splice)
     2. 这会删除 tool_use 块,导致后续的 tool_result 找不到对应的 tool_use_id
     3. 也会删除用户的文本消息,导致上下文丢失
重要背景(官方文档确认)
     根据 Claude API 官方文档:
     - 最多可定义 4 个 cache_control 断点
     - 如果超过限制,API 不会报错,只是静默地忽略多余的断点
     - "20 个块回溯窗口" 是缓存命中检查的范围,与断点数量限制无关
     因此,这个函数的原始设计(删除内容块)是不必要且有害的。
修复方案:
     保留函数但修改行为:只删除 cache_control 属性,保留内容本身
修改位置;
     文件: src/services/claudeRelayService.js
修改内容:
     将 removeFromMessages() 和 removeFromSystem() 函数从"删除整个内容块"改为"只删除 cache_control 属性":
     // 修改前:直接删除整个内容块
     message.content.splice(contentIndex, 1)
     // 修改后:只删除 cache_control 属性,保留内容
     delete contentItem.cache_control
效果对比;
     | 场景         | 修复前            | 修复后            |
     |------------|----------------|----------------|
     | 用户文本消息     |  整个消息被删除      |  保留消息,只移除缓存标记 |
     | tool_use 块 |  被删除导致 400 错误 |  保留完整内容       |
     | system 提示词 |  整个提示词被删除     |  保留提示词内容      |
     | 缓存功能       | ⚠️ 强制限制        |  降级(不缓存但内容完整) |
2025-11-29 17:50:45 +08:00
shaw
49645e8a50 feat: 增强claude转发特征模拟 2025-11-28 13:54:42 +08:00
shaw
7db70e2dc0 feat: 为claude类型账号增加测试功能 2025-11-28 10:51:01 +08:00
shaw
4d21c85f83 fix: claude转发移除x-authorization 头 2025-11-26 19:38:28 +08:00
shaw
b61a3103e9 feat: claude转发增加runtimeAddon 2025-10-19 18:05:19 +08:00
shaw
edf302fd6b chore: 去除claude转发冗余代码 2025-10-19 17:43:13 +08:00
shaw
2ec17360d6 fix: 修复oauth的claude账号在apikey最后使用显示未已删除的bug 2025-10-17 23:14:39 +08:00
shaw
a67c34bee1 fix: 修复claude SSE捕获usage问题 2025-10-12 23:05:48 +08:00
shaw
56fe7be8ec fix: 优化claude code系统提示词判断 2025-10-11 17:34:17 +08:00
shaw
cd9a2025b2 fix: 适配droid调用claude code订阅接口 2025-10-11 14:17:08 +08:00
shaw
9b211b063b feat: 适配claude的400错误码 2025-10-11 13:00:02 +08:00
shaw
6e770146fd fix: 优化cache control问题2 2025-10-07 15:14:08 +08:00
shaw
88429e1a24 fix: 优化cache control问题 2025-10-07 14:37:28 +08:00
shaw
2872198259 chore: claude绑定账号响应限流提示 2025-10-04 11:31:21 +08:00
shaw
cd72a29674 chore: opus周限提示增加重置时间 2025-10-04 11:10:55 +08:00
shaw
d44582dc31 feat: 适配claude新opus周限规则 2025-10-04 10:49:40 +08:00
Gemini Wen
87bd54d9ea fix: 修复统一客户端标识的布尔值判断
将 useUnifiedClientId 的判断从直接布尔值比较改为字符串 'true' 比较,修复配置值为字符串时的判断问题。

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-30 13:01:54 +08:00
shaw
4ab91f233f refactor: 使用 claudeCodeValidator 统一验证逻辑
替换 _hasClaudeCodeSystemPrompt 方法,改用 claudeCodeValidator

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-30 11:25:35 +08:00
shaw
8b8e9703a1 fix: claude遇到5xx错误不再停止调度 2025-09-24 14:37:33 +08:00
shaw
00faa21e4b fix: 修复claude账号限流不会自动恢复的bug 2025-09-24 09:36:42 +08:00
shaw
24d1f0a494 fix: 修复统一user-agent问题 2025-09-23 17:35:10 +08:00
shaw
5a2199f9a9 fix: claude提示词检测逻辑修复 2025-09-23 16:29:17 +08:00
shaw
0eb95b3b06 refactor: 清理模型限制检查的冗余代码
优化内容:
- 删除 claudeRelayService.js 中的重复模型限制检查(82行代码)
- 保留 api.js 中的统一检查,覆盖所有服务类型(claude/console/ccr)
- 移除 /v1/messages/count_tokens 端点的模型限制(计数接口不需要限制)

架构改进:
- 模型限制逻辑现在集中在 api.js 的 handleMessagesRequest 函数中
- 避免了每个服务各自实现一遍的重复代码
- 提高了代码的可维护性和一致性

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 09:43:15 +08:00
shaw
f667a95d88 fix: 修复模型限制功能逻辑错误(从白名单改回黑名单)
问题原因:
- 在提交 7f9869ae 添加CCR支持时,错误地将模型限制从黑名单改成了白名单
- 前端UI显示"设置此API Key无法访问的模型",明确表示这是黑名单
- 后端却将其当作白名单处理,导致逻辑完全相反

修复内容:
- 将判断逻辑从 !includes 改回 includes(黑名单逻辑)
- 更新注释和日志消息,明确这是"限制列表"而非"允许列表"
- 同时修复了 api.js 和 claudeRelayService.js 中的所有相关判断

影响范围:
- src/routes/api.js: 修复 /v1/messages 和 /v1/messages/count_tokens 端点的模型限制判断
- src/services/claudeRelayService.js: 修复流式和非流式请求的模型限制判断

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 09:35:04 +08:00
Wesley Liddick
fe1f05fadd Merge pull request #411 from bottotl/main
兼容 sider 自定义 API
2025-09-10 22:36:54 +08:00
root
4fd4dbfa51 fix: 回退401错误处理逻辑到原始版本
- 恢复"遇到1次401就停止调度"的原始逻辑
- 移除"记录401错误但不停用账号"的临时修改
- 修复非流式和流式请求中的401处理逻辑
- 确保401错误会立即标记账号为异常状态

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 08:20:17 +00:00