Skip to content

compaction_trigger requests pass through preflight/clamp before compact detection — mutated payload, upstream 400 loop #333

Closed

Description

@ranxianglei

Model: glm-5.3

现象

codex 的原生压缩请求(compaction_trigger 形态)在进入压缩检测/门控之前先流经 preflight 溢出压缩与载荷重建——即使最终走透传,上游收到的也是一个被改写过的压缩请求。

证据(真机 e2e 捕获(本地 sglang qwen3.8-27b @8199),codex 0.147 → bili → 本地 sglang(qwen3.8-27b))

e2e-codex-Fmg1r6 会话(窗口 32000),时序完整:

04:13:10.644 [warn] context 44354 tokens exceeds model window 32000 (model=qwen3.8-27b);
       preflight compressing before forward            ← 这其实是 codex 的 compaction_trigger 请求
04:13:25.792 [info] [acp-compress-obs] shrink 24% (~10864/44354 tok) foldPoint=m00026 blocks=1
04:13:25.793 [info] preflight compressed 1 range(s), ~10188 tokens saved (44354 → 34166);
       rebuilding payload                               ← 压缩请求自身被重建
04:13:25.794 [info] forward POST → http://<private-host>:8199/v1/responses
04:13:25.797 [warn] ← upstream 400: {"error":{"message":"Unsupported Responses API input item
       type: 'compaction_trigger' None","type":"invalid_request_error","code":400}}
(codex 重试 → 400 ×3,循环)

e2e-codex-1njUcz、4mJRDY、aHTRg8、PAN8vm、PKuwOo 等多次运行同样模式。

两个问题

  1. 检测顺序:compaction 检测/门控在 preflight 之后执行,preflight 先把载荷折了再重建。pass 模式下上游收到的是"被 bili 改写过的压缩请求"——与 codex 本地持有的历史不再一致。
  2. 上游 400 循环(sglang 不认 compaction_trigger 输入项):非 OpenAI 后端不支持 compaction_trigger 输入项,任何透传都会 400。codex 重试 3 次全部 400,该轮任务失败。这正是 E2 拦截伪造要解决的场景(feat: intercept + forge codex native compaction when ACP is healthy (#321 PR-E2) #325),但在 E2 未启用/门控失败时的兜底行为不该是"改写后转发"。

修复方向(供讨论)

  • 压缩请求检测前置到 preflight/窗口钳制之前,然后三路分发:
    • E2 健康 + 门控通过 → 拦截伪造(现有逻辑)
    • 门控失败 → 原样透传(不 preflight、不重建、不钳制窗口)
    • 非 codex 客户端 → 正常管线
  • pass 模式下对 compaction_trigger 请求至少做到零改写。

关联

Activity

  1. ranxianglei commented on Aug 29, 2026

    @ranxianglei
    OwnerAuthor

    🤖 ework agent · vllm-qwen/qwen3.8-27b

    [bot] 收到,正在处理。

    初步定位:E2 拦截/门控逻辑在 PR #325 的分支 2026-08-28_codex-compact-intercept 上(尚未合入 master),而 preflight 溢出压缩与载荷重建在 src/server.ts 主管线中先于 compact 检测执行。我会把 compaction 检测前置到 preflight/窗口钳制之前,按 issue 里的三路分发实现:

    • E2 健康 + 门控通过 → 拦截伪造(现有逻辑)
    • 门控失败 → 原样透传(不 preflight、不重建、不钳制窗口)
    • 非 codex 客户端 → 正常管线

    修完后在 #325 分支上追加提交并验证(typecheck / test / build),进展会同步到这里。

  2. changed the title [-]compaction_trigger requests pass through preflight/clamp before compact detection — mutated payload, relay 400 loop[/-] [+]compaction_trigger requests pass through preflight/clamp before compact detection — mutated payload, upstream 400 loop[/+] on Aug 29, 2026
  3. ranxianglei commented on Aug 29, 2026

    @ranxianglei
    OwnerAuthor

    🤖 ework agent · vllm-qwen/qwen3.8-27b

    [bot] 已修复,PR: #335

    修复方式(按 issue 里的三路分发方向实现)

    检测前置到 per-session 管线顶部(runPrepare()/preflight 之前),对原始解析后的 body 判定 isCodexClient && 最后一个 input item === compaction_trigger,然后三路分发:

    1. E2 开启 + 门控前置条件通过 → 走 prepareResponses(transform + 现有伪造逻辑)→ 本地返回伪造的 2 帧 SSE,不碰上游
    2. pass 模式 或 门控前置条件失败 → 原样透传:转发原始请求字节(复用 prepared=null 透传路径,同 fix: stitched-stream usage double-count after compress re-request (adapter-anthropic >0 guard) #300 hop-marker),不 preflight、不重建、不钳制窗口,也不作为副作用折叠 session state
    3. 非 codex 客户端 / 非压缩请求 → 正常管线,完全不变

    改动

    • src/codex-compact.ts:安全阀拆成 codexCompactGatePre(session, limit)(limit>0、稳态 <90% 窗口、有 active block —— 全部是 transform 之前就可判定的条件)+ codexCompactGate = transformOk && gatePre(语义与原来完全一致)
    • src/server.ts 管线:前置检测 + 三路分发;未拦截时打日志说明原因(BILI_CODEX_COMPACT=pass / gate preconditions not met / transform/forge failed)
    • src/server.ts prepareResponsesCompact(endpoint 形态):门控前置条件移到 processTurn 之前检查 —— 不被拦截的 compact 请求不再作为副作用变更 session state(线上行为不变:仍是原始 body 透传)

    回归测试(4 个新 e2e)

    关键:现有 e2e 用 10k 窗口 + ~7700 tok 会话,preflight 从不触发,正好漏掉本 bug。新测试用超窗载荷(14 条消息 ≈15.4k tok > 10k 窗口,即旧顺序触发 preflight 的条件):

    • pass 模式 + 超窗 compact → 上游收到的 body 与 codex 发出的逐字节一致,session state 无变化
    • intercept + 门控失败(≥90%)+ 超窗 compact → 逐字节透传(即 issue 里 44354>32000 的真实日志场景,不再"改写后转发"→400 循环)
    • intercept + ACP 健康 + 超窗 compact → 正常伪造,上游不接触(preflight 不再干扰伪造路径)
    • endpoint 形态 + 门控失败 → 原始 body 透传,processTurn 被跳过(无 state 变更)

    4 个测试在旧代码上全部失败(stash 掉 server.ts 改动验证过),修复后通过。

    Pre-flight

    • npm run typecheck 干净
    • npm test 761/761 通过
    • npm run build 干净

    两个范围外说明

    1. 分支叠加:本 PR 基于 2026-08-28_codex-compact-intercept(PR feat: intercept + forge codex native compaction when ACP is healthy (#321 PR-E2) #325,E2 拦截伪造本体)。feat: intercept + forge codex native compaction when ACP is healthy (#321 PR-E2) #325 先合、或两个一起合。
    2. x-codex-beta-features: remote_compaction_v2 头剥离(buildForwardTarget 里对所有 codex 请求生效)保持原样未动 —— 那是既有行为且有独立的聚合器拒绝理由。但如果 pass 模式透传到要求该 beta 头的后端(如真 OpenAI)发现 native compaction 需要保留它,可以后续单独处理。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions