Skip to content

feat(responses): 实现响应状态续接路由 - #609

Merged
tbphp merged 4 commits into
mainfrom
tbphp/responses-state-continuation
Sep 9, 2026
Merged

feat(responses): 实现响应状态续接路由#609
tbphp merged 4 commits into
mainfrom
tbphp/responses-state-continuation

Conversation

@tbphp

@tbphp tbphp commented Sep 9, 2026

Copy link
Copy Markdown
Owner

关联 Issue / Related Issue

无关联 Issue。

变更内容 / Change Content

  • Bug 修复 / Bug fix
  • 新功能 / New feature
  • 其他改动 / Other changes

HTTP/SSE Responses 的后续请求原先可能被调度到无法访问上一轮响应的凭据。本次按 AccessKeyID + response_id 记录归属,续接仅使用当前路由仍允许的原凭据;例如首轮由 A 生成响应,即使下一次普通轮询应选 B,续接也只能使用 A。

  • ID 路由与提示词软亲和互斥,不受亲和开关影响;实际命中时复用现有亲和图标。继续使用原候选过滤、健康裁决、共享记账和受限认证刷新重试,普通失败不会换凭据。
  • 响应 ID 在 JSON/SSE 下发前写入有界内存索引,复用正常停机 checkpoint 恢复。请求路径不增加 DB 或文件 IO,不新增依赖、数据表或迁移。
  • 管理面保存与匹配规则应用时保护 previous_response_id。旧覆盖配置仍可加载和查看;应用时允许旧规则删除不存在的字段,仍拒绝删除实际存在的字段(包括 null 和空字符串)或通过 set 注入该字段。
  • 同步更新中文、英文、日文 README。

兼容性与边界:

  • 按原生 Responses Create 和现有 upstream_managed 存储声明自动接入,当前包括 openaigpt_loadxainewapicliproxyapisub2api,不新增渠道开关。中转渠道仅保证回到原下一跳,实际状态可用性由上游决定。无状态、转换以及明确 store:false 的响应不登记新 ID;Codex 订阅的 WebSocket 接入留在后续阶段。
  • 纯 ID 续接不再要求查询、删除、取消等完整生命周期能力,调度与执行器使用同一存储要求。与 conversationbackground、保存的 prompt 或其他资源引用混用时继续保留原条件,其他 Responses 资源操作的归属路由不在本次范围。
  • 未知、过期或淘汰的 ID 本地拒绝,包括升级前和网关外创建的 ID;非字符串、非 null 值及重复字段返回本地 400。
  • 绑定默认保留 30 天,最多 100,000 条、单个 ID 最大 4 KiB、ID 文本合计 16 MiB。超长 ID 拒绝登记和下发,快照恢复时跳过。仅承诺正常停机成功保存 checkpoint 后恢复,不保证崩溃恢复或上游历史持续有效。

验证:make check 通过。Go 回归覆盖多凭据续接、AccessKey 隔离、亲和互斥、SSE 登记时点、冷却恢复、身份变更、失败不换号、参数覆盖、索引边界和 checkpoint 恢复。六个渠道均通过实际 Bifrost 执行链路连接本地 HTTP 上游,验证连续请求保留原凭据和 previous_response_id,store:false 仍可使用已有绑定且不登记新 ID;混合资源、无状态和转换候选继续受限。这些验证不代表真实上游账号的状态存储保证。未运行本地 race 或前端测试。

自查清单 / Checklist

  • 我已运行 make check,或在说明中写明无法运行的原因和未验证范围。 / I ran make check, or documented why it could not run and what remains unverified.
  • 本 PR 范围聚焦,未包含无关改动。 / This PR is focused and contains no unrelated changes.
  • 我已更新必要的公开文档或发布说明。 / I updated any required public documentation or release notes.
  • 我已确认提交、日志和测试数据不包含敏感信息。 / I confirmed that commits, logs, and fixtures contain no sensitive data.
  • 如适用,我已说明兼容性或数据迁移影响。 / Where applicable, I documented compatibility or data-migration impact.

Copilot AI lite review requested due to automatic review settings September 9, 2026 11:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T15:13:03.761776Z 73ee9b6 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

📝 Walkthrough

Walkthrough

本次变更为 OpenAI Responses 增加 previous_response_id 续接支持。系统按 AccessKey 记录响应归属,并在后续请求中固定允许的凭据。响应归属支持 TTL、容量限制、LRU 淘汰和运行时检查点恢复。参数覆盖不能修改 previous_response_id。README 文档同步说明支持范围和限制。

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 73ee9

Empty continuation IDs may bypass local validation, and the documentation advertises continuation channels beyond the stated support scope. Resolve these contract mismatches before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.64% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 55 functions across 35 files. (3 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed 标题“feat(responses): 实现响应状态续接路由”准确概括了本次 PR 的主要变更,内容明确且简洁。
Description check ✅ Passed 描述已说明新功能、实现范围、兼容性边界、验证结果和自查清单。描述明确记录了未运行 race 测试和前端测试的范围;“无关联 Issue”也已明确说明。
Full details: Docstring Coverage

Explanation

Docstring coverage is 3.64% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 55 functions across 35 files. (3 skipped: 3 unsupported.)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
internal/dialect/request_fields.go-79-80 (1)

79-80: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

拒绝空的 previous_response_id

当客户端发送 {"previous_response_id":""} 时,Decode 会成功,但 PreviousResponseID 保持为空。后续代码将空值视为非续接请求,因此跳过响应归属查找并把无效字段转发到上游,而不是在本地拒绝格式非法的 ID。

请在保留 null 支持的同时拒绝空字符串,并增加该用例的测试。

建议修改
-			if err := decoder.Decode(&result.PreviousResponseID); err != nil {
+			var previousResponseID *string
+			if err := decoder.Decode(&previousResponseID); err != nil {
 				return RequestMetadata{}, fmt.Errorf("previous_response_id must be a string or null")
 			}
+			if previousResponseID != nil {
+				if *previousResponseID == "" {
+					return RequestMetadata{}, fmt.Errorf("previous_response_id must be non-empty")
+				}
+				result.PreviousResponseID = *previousResponseID
+			}

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: 2b7fcce6-71a6-4baa-b60e-91de4b1d9f6f

📥 Commits

Reviewing files that changed from the base of the PR and between d417b7d and 69cb135.

📒 Files selected for processing (30)
  • README.md
  • README_CN.md
  • README_JP.md
  • internal/app/runtime_checkpoint.go
  • internal/app/runtime_checkpoint_test.go
  • internal/app/scheduling_checkpoint_test.go
  • internal/container/container.go
  • internal/control/group_create.go
  • internal/control/group_settings_test.go
  • internal/dialect/anthropic.go
  • internal/dialect/dialect.go
  • internal/dialect/openai.go
  • internal/dialect/openai_embeddings.go
  • internal/dialect/openai_images.go
  • internal/dialect/openai_responses.go
  • internal/dialect/openai_responses_test.go
  • internal/dialect/request_fields.go
  • internal/dialect/request_fields_test.go
  • internal/dialect/rerank.go
  • internal/gateway/execution_forward.go
  • internal/gateway/forward.go
  • internal/gateway/handler.go
  • internal/gateway/reason.go
  • internal/gateway/reason_test.go
  • internal/gateway/responses_continuation.go
  • internal/gateway/responses_continuation_test.go
  • internal/parameteroverride/response_continuation_test.go
  • internal/parameteroverride/rules.go
  • internal/state/response_bindings.go
  • internal/state/response_bindings_test.go

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment thread internal/state/response_bindings.go
@tbphp tbphp self-assigned this Sep 9, 2026
@tbphp tbphp added the enhancement New feature or request label Sep 9, 2026
@tbphp tbphp added this to the v2.0.0 milestone Sep 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: c6d4b18d-faa4-4862-855e-bf7eb0d01e79

📥 Commits

Reviewing files that changed from the base of the PR and between 7f28bbd and a9bc90f.

📒 Files selected for processing (16)
  • README.md
  • README_CN.md
  • README_JP.md
  • internal/dialect/request_execution.go
  • internal/dialect/request_execution_test.go
  • internal/execution/bifrost/executor.go
  • internal/execution/contracts.go
  • internal/execution/contracts_test.go
  • internal/execution/validation.go
  • internal/gateway/execution_forward.go
  • internal/gateway/forward.go
  • internal/gateway/handler.go
  • internal/gateway/responses_continuation.go
  • internal/gateway/responses_continuation_test.go
  • internal/scheduler/channel_scheduler_test.go
  • internal/scheduler/inspect.go

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.

Comment thread README.md
@tbphp

tbphp commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

针对审查汇总中剩余两项建议,保持现有实现:

  • previous_response_id:当前契约将缺省、null 和空字符串视为未提供续接 ID;非字符串、非 null 值以及重复字段仍在本地拒绝。相关回归测试 已通过,保留这一兼容行为。
  • 注释覆盖率 80%:该阈值不是仓库质量门禁。当前提交 a9bc90famake check 已通过,不为该阈值批量添加注释。

@tbphp
tbphp merged commit 7cbd2e6 into main Sep 9, 2026
11 checks passed
@tbphp
tbphp deleted the tbphp/responses-state-continuation branch September 9, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants