新闻详情

新闻详情

首页 / 资讯中心 / 详情

opencodex 上游请求重试与错误重构:共享 retry 层设计解析

发布时间:2026/11/12 18:00:56来源:尧图网络
opencodex 上游请求重试与错误重构:共享 retry 层设计解析
opencodex 上游请求重试与错误重构共享 retry 层设计解析【免费下载链接】opencodexUniversal provider proxy for OpenAI Codex Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code项目地址: https://gitcode.com/gh_mirrors/ope/opencodex导读本文围绕 opencodex 中一次关键的共享上游重试/错误重构shared upstream retry/error refactor即 PR #98展开剖析该代理在向上游提供商ChatGPT、Claude、Gemini 等发起 fetch 时如何在不破坏幂等语义的前提下安全地处理连接重置与瞬时 5xx。读完本文你将掌握 opencodex 的重试边界划分、发送预算send budget机制、Retry-After处理策略以及这些设计在源码与测试中的落地形态。背景为什么需要一个共享的重试层在 opencodex 中代理进程会向上游托管服务发起大量模型请求。重构前的痛点主要有两个Bun fetch 连接池复用半关闭 socketchatgpt.comCloudflare 托管会在服务端主动关闭空闲的 keep-alive 连接而 Bun 的 fetch 连接池可能复用到这条半关闭连接导致请求在响应头到达前就失败The socket connection was closed unexpectedly/ECONNRESET。重试策略散落各处、边界模糊不同适配器各自实现重试容易出现请求已经被上游执行却因为传输层错误被再次发送的重复计费问题——模型 POST 请求天然非幂等。PR #98 的价值在于把重试/错误处理收敛为一个共享的 leaf 模块src/lib/upstream-retry.ts并配套共享的 HTTP 错误归一化工具src/adapters/upstream-http-error.ts。该模块被kiro-retry等适配器层复用因此被要求MUST stay a leaf module不导入server.ts或任何适配器保证依赖方向单向、无循环。重试边界什么可以重试什么绝不能重试upstream-retry.ts的开篇注释给出了整个模块的哲学响应头到达前的拒绝并不能证明上游没有处理该请求。因此模块刻意收窄重试范围可重试明确可安全重放replaySafe的操作上的连接重置ECONNRESET/EPIPE以及瞬时 5xx状态码不可重试超时TimeoutError、主动中止AbortError、ECONNREFUSED、DNS/TLS 失败以及以Response形式返回的 HTTP 错误状态它们从不被 throw响应头已到达之后的流中断不在本模块范围内因为响应已经 resolve这一阶段由 Responses 传输层通过共享的 resend gate 处理。连接重置的识别集中在isConnectionResetErrorsrc/lib/upstream-retry.tsexport function isConnectionResetError(err: unknown): boolean { if (!(err instanceof Error)) return false; // Aborts and timeouts are caller decisions / honest failures — never retryable. if (err.name AbortError || err.name TimeoutError) return false; const code (err as { code?: unknown }).code; if (code ECONNRESET || code EPIPE) return true; const msg err.message.toLowerCase(); return msg.includes(socket connection was closed unexpectedly) || msg.includes(connection reset by peer); }注意一个细节即使错误带有ECONNRESET的code只要其name是TimeoutError/AbortError也会被判定为不可重试——重试决策以错误语义为准而非以错误码为准。这一行为在 tests/lib/upstream-retry.test.ts 中有专门的测试用例覆盖。不可重放响应标记体系拒绝自动补发对于上游可能已经执行的模糊重置模块提供了一套不可重放标记机制贯穿进程内与跨层重包装两个场景进程内标记nonReplayableResponses与replayRefusalResponses两个WeakSetResponse分别回答绝不能再次发送与上游从未返回过这个响应两个问题。后者的存在是为了防止配额记录器把合成的 429 误当成真实限流、或客户端误读Retry-After。跨层标记三个结构化错误码能在响应体被重包装如 combo 失败消费重新解析 JSON后继续存活export const UPSTREAM_NO_RESPONSE_CODE upstream_no_response; export const UPSTREAM_CLOSED_BEFORE_RESPONSE_CODE upstream_closed_before_response; export const UPSTREAM_RESET_REPLAY_REFUSED_CODE upstream_reset_replay_refused;当代理拒绝重放模糊重置时返回429状态并附加x-should-retry: false头REPLAY_REFUSAL_STATUS 429src/lib/upstream-retry.ts。选择 429 而非 5xx 是刻意的Codex 客户端配置为retry_429: false、retry_5xx: true四轮尝试若返回 5xx 会放大重复发送。而x-should-retry: false是 Stainless 生成的客户端openai/anthropic的 Python 与 Node 版本在读取状态码表之前就会校验的信号即便 429 不带Retry-After客户端也不会自动补发。applyReplayRefusalClientHeaders把删除retry-after与设置x-should-retry: false捆绑为一个原子操作——两者必须同时成立否则客户端仍可能把可能已执行的 turn 再次发出。发送预算把重试次数变成一个真实的上游发送上限多层重试最容易出的问题就是相乘爆炸外层 transient 重试 3 轮、每轮内部又独立重试 3 次连接重置一次逻辑请求最多发出 9 个物理发送。重构的核心修复是attempts是总发送预算而非每层重试次数fetchWithTransientRetry把remaining()预算减去已发送数转发给内部的fetchWithResetRetry两层共用同一个计数发送前计数countedFetch在await之前sent 1因为发送但被拒绝依然消耗预算——只统计成功会让重置风暴无限循环零就是零normalizeSendAttempts移除了旧的Math.max(1, ...)下限预算耗尽直接抛SendBudgetExhaustedError而不是再免费送一次发送单次上报内部的 reset 层不再转发onSendsConsumed避免同一物理发送被上报两次导致四发送上限变成两发送上限。预算跨调用帧共享TransientSendBudget是可变持有者{ used: number }因为 combo 父级会为每个目标发起独立的子 turn若计数器局限在单帧内一个逻辑请求就能对每个目标发出三次上游请求对应 issue #4546。此外claimAmbiguousResend回调让逻辑请求持有一次操作者授权的模糊重置替换额度由重置层与 post-header 协议 gate 共用同一计数器——三处rotation 腿、refresh 腿、同目标 429 腿持有同一 turn就不能各自消耗一次替换额度。瞬时 5xx 重试层状态表、慢速失败与 Retry-AfterfetchWithTransientRetry处理响应头已收到但状态为瞬时错误的场景仅 pre-stream。瞬时状态表定义在isTransientUpstreamStatussrc/lib/upstream-retry.ts500, 502, 503, 504, 520, 521, 522其中 500 遵循 OpenAI SDK 默认500 自动重试507 虽然在 48 小时日志中观测到但被刻意排除存储类错误而非网关瞬时错误。三个关键决策值得注意慢速失败直接返回TRANSIENT_RETRY_SLOW_ATTEMPT_MS 15_000——一次尝试慢于 15 秒slow 502事故形态观测到 191 秒时不再重试避免在客户端超时之后重复压榨上游Retry-After是下界而非可压缩值retryAfterIsLowerBound: true时指令等待时间原样返回Math.max(retryAfter, jittered)本地指数退避上限无权缩短提供商的指令等待截止时间是调用方决策retryAfterCeilingMs默认RETRY_AFTER_CEILING_MS 60_000与同目标 429 上限一致。它是截止时间而非钳位器指令超过截止时间就带着完整的Retry-After结束调用绝不提前发送——因为提前发送是提供商已经说过会拒绝的请求。退避计算在retryBackoffDelayMssrc/lib/upstream-retry.ts指数退避baseDelayMs * 2^attempt加上 0.8–1.2 的抖动jitter优先遵循响应头中的Retry-After支持秒数与 HTTP-date 两种格式未提供时才回退到本地指数退避。连接重置腿的退避参数为初始 150ms、上限 1s、最多 3 次尝试RESET_RETRY_MAX_ATTEMPTS 3注释说明连接池可能持有不止一条陈旧 sockettransient 层为初始 400ms、上限 5s、最多 3 次总发送TRANSIENT_RETRY_MAX_ATTEMPTS 3。保留上游证据UpstreamRetryEvidenceError当重试耗尽且此前尝试已产生凭证可见的证据transient 5xx 响应或请求已被读取后的连接重置终端拒绝会被包装为UpstreamRetryEvidenceError源自 PR #966作者 Yuxin-Qiao见 src/lib/upstream-retry.ts原始拒绝保留在cause中错误码与消息仍可检查混合 5xx/重置导致拒绝时不得降级为账号无关的 pre-connection 类错误issue #914 评审结论因为证据表明主机与凭证路径已被触达SendBudgetExhaustedError是例外预算拒绝不是上游证据必须保持可识别不能被错误归类。共享的 HTTP 错误归一化与脱敏与重试层配套的共享错误工具在 src/adapters/upstream-http-error.tsnormalizeUpstreamHttpErrorResponse重建错误响应时剥离content-encoding与content-length避免已编码/长度不符的载荷误导下游但保留retry-after、x-provider-error等安全头部sanitizeUpstreamErrorText对错误文本做脱敏抹掉Authorization: Bearer ...这类凭证与绝对路径macOS/Users/...与 WindowsC:\...风格均覆盖防止上游错误细节泄入客户端日志或 UI。对应测试在 tests/adapters/upstream-http-error.test.ts覆盖脱敏与头部剥离行为。测试与落地验证共享重试模块的测试在 tests/lib/upstream-retry.test.ts647 行覆盖isConnectionResetError对 Bun 重置错误形态含消息匹配、code属性缺失、非 Error 输入的分类sleepWithHeartbeats对非正数/NaN 心跳间隔的钳位防止零步长死循环预算耗尽与不可重放标记的断言HTTP 错误归一化测试在 tests/adapters/upstream-http-error.test.ts。落地记录可参考 devlog/_fin/260712_pr_batch_landing/000_plan.mdPR #98commit6bb3aecf在 #96、#97 之后以 merge-only 方式合入 mainmerge SHA576d8c45评审结论为 MERGE-WITH-NITS行为等价nit 为适配器级回归测试延期处理。合入时验证命令为bun test tests/upstream-retry.test.ts tests/upstream-http-error.test.ts当前仓库中对应文件位于tests/lib/与tests/adapters/随后跑完整bun test验收标准为全量套件 0 失败。总结opencodex 的共享上游重试/错误重构提供了一套可复用的范式以幂等语义为第一原则划定重试边界用发送预算把多层重试压缩为真实发送上限用标记体系阻止对可能已执行请求的自动补发。任何需要直连上游 fetch 的适配器如kiro-retry都可以直接复用 src/lib/upstream-retry.ts而无需各自重新实现退避、Retry-After与证据保留逻辑。【免费下载链接】opencodexUniversal provider proxy for OpenAI Codex Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code项目地址: https://gitcode.com/gh_mirrors/ope/opencodex创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/11 13:52:51

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/10 19:42:40

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/12 9:50:02

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/12 9:25:38

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/12 15:57:44

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/10 17:14:10

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉