feat: 新增 Thinking 整流器处理跨 Provider 签名错误 (#115)

当 Provider A 生成的 thinking 块被发送到 Provider B 时,签名验证会失败。
本次更新实现了自动整流机制,在遇到签名错误时自动清洗 thinking 块后重试。

主要更改:
- 新增 ThinkingRectifier 整流器,移除 thinking 块和 signature 字段
- 新增 ThinkingSignatureException 异常类型
- ErrorClassifier 新增 Thinking 错误模式检测
- FallbackOrchestrator 支持整流后在当前候选重试
- Handler 层传递 request_body_ref 容器支持请求体动态修改
- Usage API 新增 has_rectified 字段标识整流过的请求
- 新增 THINKING_RECTIFIER_ENABLED 配置项控制功能开关

其他改进:
- CacheAwareScheduler 支持 exact/convertible 候选分组排序
- StreamProcessor 预读阶段新增格式转换试验
- ProviderAPIKey.api_formats 改为可空(None 表示支持所有格式)
- Dockerfile 修复 entrypoint.sh 换行符问题

Closes #115
Co-Authored-By: FredericMN <FredericMN@users.noreply.github.com>
This commit is contained in:
fawney19
2026-01-22 14:17:59 +08:00
parent cc5db20c58
commit af1828dd32
19 changed files with 1211 additions and 45 deletions

View File

@@ -1084,10 +1084,12 @@ class CacheAwareScheduler:
continue
# Key 直属 Provider通过 api_formats 按端点格式筛选
# api_formats=None 视为"全支持"(兼容历史数据)
active_keys = [
key
for key in provider.api_keys
if key.is_active and endpoint_format_str in (key.api_formats or [])
if key.is_active
and (key.api_formats is None or endpoint_format_str in key.api_formats)
]
if not active_keys:
continue
@@ -1264,21 +1266,31 @@ class CacheAwareScheduler:
"""
根据优先级模式对候选列表排序(数字越小越优先)
- provider: 提供商优先模式,保持原有顺序(按 Provider.provider_priority -> Key.internal_priority 排序,已由查询保证)
Key.internal_priority 表示 Endpoint 内部优先级,同优先级内通过哈希分散负载均衡
- global_key: 全局 Key 优先模式,按 Key.global_priority_by_format 升序排序(数字小的优先)
有优先级的优先NULL 的排后面
同优先级内通过哈希分散实现负载均衡
排序规则:
1. exact 候选needs_conversion=False优先于 convertible 候选
2. 在同一类型内,按优先模式排序:
- provider: 提供商优先模式,按 Provider.provider_priority -> Key.internal_priority 排序
- global_key: 全局 Key 优先模式,按 Key.global_priority_by_format 排序
"""
if not candidates:
return candidates
if self.priority_mode == self.PRIORITY_MODE_GLOBAL_KEY:
# 全局 Key 优先模式:按 global_priority 分组,同组内哈希分散负载均衡
return self._sort_by_global_priority_with_hash(candidates, affinity_key, api_format)
# 按 needs_conversion 分组exact 优先
exact_candidates = [c for c in candidates if not c.needs_conversion]
convertible_candidates = [c for c in candidates if c.needs_conversion]
# 提供商优先模式保持原有顺序provider_priority 排序已经由查询保证)
return candidates
if self.priority_mode == self.PRIORITY_MODE_GLOBAL_KEY:
# 全局 Key 优先模式:分别对两组排序后合并
sorted_exact = self._sort_by_global_priority_with_hash(
exact_candidates, affinity_key, api_format
)
sorted_convertible = self._sort_by_global_priority_with_hash(
convertible_candidates, affinity_key, api_format
)
return sorted_exact + sorted_convertible
# 提供商优先模式exact 在前convertible 在后(各组内部顺序已由构建时保证)
return exact_candidates + convertible_candidates
def _sort_by_global_priority_with_hash(
self,