fix: 修掉模型调用链路上的四处失效逻辑

- 超时:NewGateway 里 gw_timeout*time.Second 把已是 Duration 的值二次换算,
  int64 溢出后无论 OT_PROXY_TIMEOUT 填多少都等于没有超时,渠道 timeout_ms
  也从没被读过。现在非流式用 context deadline 约束整段时长,流式只约束首包
  (Transport.ResponseHeaderTimeout),正文不限时长,长回答不会被砍。
- 断连:上游请求改用 NewRequestWithContext 绑定客户端 context,客户端断开即
  取消上游并记 canceled,不再空转生成继续付费;PATCH 已写 200 头后不再往
  响应里追加错误体。
- 白名单:APIKey.AllowedModels 以前只存不用,现在 Auth 注入 context 并参与
  判定(用户黑名单 > 密钥白名单 > 用户白名单 > 全局)。
- 选路:模型已有渠道绑定时只用绑定渠道,都不可用直接 503 no_channel,不再
  把请求发给不认识它的渠道;新增运行期健康(连续 3 次网络错误/超时/429/5xx
  进 30s 冷却,成功清零),与 60s 一次的 /models 探测互补。
- 密钥 API:PATCH 回显更新后的完整对象;限额填 0 统一视为不限制(原来
  token 配额存 0 会让密钥第一次请求就被 429 打死,而请求配额存 0 又等于
  不限制,两个字段语义相反);前端编辑框总是提交白名单,清空能真的取消。

测试:新增 proxy 链路集成测试(超时生效、流式不被截断、断连取消并记账、
密钥白名单 403、连续 429 后候选剔除)与 channel 用例(绑定模型不退全局、
冷却阈值与到期恢复、成功清零)。
This commit is contained in:
Sakurasan committed 2026-10-16 19:15:51 +08:00
1 parent d911881499
commit e865f69a41
10 files changed
+659 -69

No files matched your search

+10 -4
View File
@@ -107,7 +107,8 @@ async function saveEdit() {
const m = parseModels(editForm.allowed_models)
if (t !== undefined) payload.quota_tokens_per_day = t
if (r !== undefined) payload.quota_requests_per_day = r
if (m !== undefined) payload.allowed_models = m
// 白名单框预填的是当前值,内容即最终意图:清空要能取消限制,所以总是提交
payload.allowed_models = m ?? []
await http.patch(`/keys/${editing.value.id}`, payload)
toast.ok('密钥已更新')
editOpen.value = false
@@ -280,10 +281,15 @@ onMounted(load)
<div class="space-y-4">
<Input v-model="editForm.name" label="名称" />
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<Input v-model="editForm.quota_tokens_per_day" label="每日 Token 上限" placeholder="留空则不修改" />
<Input v-model="editForm.quota_requests_per_day" label="每日请求上限" placeholder="留空则不修改" />
<Input v-model="editForm.quota_tokens_per_day" label="每日 Token 上限" placeholder="留空不修改,0 = 不限制" />
<Input v-model="editForm.quota_requests_per_day" label="每日请求上限" placeholder="留空不修改,0 = 不限制" />
</div>
<Input v-model="editForm.allowed_models" label="模型白名单" placeholder="逗号分隔" hint="留空则不修改" />
<Input
v-model="editForm.allowed_models"
label="模型白名单"
placeholder="逗号分隔"
hint="留空 = 不限制模型(清空后保存即取消白名单)"
/>
<label class="block">
<span class="mb-1.5 block text-xs font-medium text-muted">状态</span>
<select