Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

[Bug]: 跨通道切换模型被 channel affinity 硬拦截,codex额度耗尽后无法切换模型继续

已关闭
#3,537 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
5/5
预计耗时
一周以上
新手友好度
35/100
Issue 类型
功能
描述清晰度
描述清楚
活跃度
活跃
技术栈
rust
领域
ai, desktop

调研方向

Start by reading the channel routing and history validation code in openbitfun_core::infrastructure::ai::channel_client_source, then trace how provider-specific reasoning items and tool results are serialized. Reproduce the Codex-to-other-channel switch with a conversation containing a tool call. Done means opaque reasoning items can be stripped while visible messages and tool results remain usable, with a notice and the requested strict-mode setting; this spans provider behavior, settings, and UI.

由索引模型根据 Issue 内容生成。

描述

Summary

环境

  • OpenBitFun 1.0.7,构建 2026-10-21T12:38:50Z,commit db7d36544896c62bc53c48c8774bcb13d4aa5e49
  • macOS,Apple Silicon
  • 通道 A:OpenAI / Codex(Responses API,回复中含 encrypted_content 推理项与签名 thinking 块)
  • 通道 B:另一家供应商(Anthropic / Gemini / 自建网关任一),凭据与 A 不同
  • 补充:本机保留的 1.0.1 安装包中不存在 channel_affinity、channel identity 相关符号,这套校验是后续版本加入的;具体引入版本未定位

现象

在通道 A 使用 Codex 模型跑过若干轮(含工具调用)后,会话中途把模型切换到通道 B 的模型并发送下一条消息,请求在发出前被拦截:

AI client error: Signed or opaque history requires its original channel and credential identity
This history requires its original channel; restore that channel or start a new conversation.
Existing history was preserved.

观察到的行为特征:

  1. 历史未被破坏(提示语明示 Existing history was preserved),但该会话在通道 B 上永久无法继续;
  2. 切回通道 A 的同一模型可以正常继续,说明拦截发生在客户端发送前,不是上游服务端报错;
  3. 界面上没有任何降级选项、没有可点击的修复动作,只有一段错误文本;
  4. 触发时机与“额度”无关,纯粹取决于历史里是否存在通道 A 签发的不可重放项。实际最常见的触发场景恰恰是“Codex 额度用完,想换别家继续”,于是这个场景被整体阻断。

从应用包可观察到的相关错误标识(strings 扫描 app bundle,仅用于定位):

channel_affinity_unavailable
Signed or opaque history has no proven channel identity
Signed or opaque history requires its original channel and credential identity
Opaque model history has no channel provenance; cross-channel routing is unavailable
Opaque model history contains incompatible channels
The manually selected channel cannot replay this opaque history
Selected channel authentication changed

模块路径:openbitfun_core::infrastructure::ai::channel_client_source

期望行为

不要拦截,改为降级并继续。

  1. 构造请求前按目标通道能力重建历史:剥离不可重放项(Responses API 的 encrypted_content 推理项、各家 thinking/thought signature),保留明文的 role 消息、tool_calls 与 tool_results;
  2. 剥离后用一条非阻断的会话内提示告知用户,例如“已切换通道,其中 N 项不可重放的推理状态未继承,可见对话与工具结果完整保留”;
  3. 仅当历史结构对目标通道根本不合法(如工具调用 schema 不被接受、多模态附件不受支持)时才失败,且失败时应给出可操作出口:一键回到原通道 / 基于摘要新建会话;
  4. 提供一个设置项:跨通道历史策略,默认「自动剥离并继续」,可选「严格拒绝(保留现状行为)」;
  5. 在诊断日志中记录剥离的条目数量与原因,便于事后排查“模型为什么忘了之前查过什么”。

影响面

  • “主通道额度耗尽 → 切备用通道继续”是高频工作流,本机制使其完全不可用,且错误信息把原因指向“通道身份”,掩盖了真实诉求;
  • 被迫新开会话的实际代价高于损失一段隐藏推理链:需要重述约束、重读文件、重新执行已做过的排查,甚至重复触发已完成的外部副作用(写文件、提交、发请求);
  • 由于错误发生在发送前且历史保留,用户既不能继续,也无法从错误中学到“该怎么切”,只能反复尝试。

现状规避(均不理想)

  • 回原通道继续:需要恢复原额度,或换同一凭据体系的可用额度;
  • 新建会话并用摘要接手:可行,但完全是手工成本,且丢失工具调用现场;
  • 回退到旧版本:行为会恢复,但放弃后续版本的全部修复,不建议。

TL;DR (EN)

In 1.0.7, switching a conversation from an OpenAI/Codex channel to a different provider is hard-blocked before the request is sent: Signed or opaque history requires its original channel and credential identity. The history is preserved, but the conversation can never continue on the new channel.

The underlying failure is real and well documented upstream (openai/codex#17541, #15219, pydantic-ai#4608/#4621, litellm incident report), but the established fix is to strip non-replayable items (encrypted_content, thinking signatures) from history and continue, not to reject the turn. Rejecting blocks the most common fallback workflow (primary quota exhausted → switch provider) and shifts the entire recovery cost onto the user, while protecting nothing that the visible transcript does not already carry.

Requested: rebuild history per target channel, drop opaque reasoning items, keep plaintext messages and tool results, emit a non-blocking notice, and only fail when the target channel truly cannot accept the structure. Add a setting: auto-strip (default) / strict reject.

Area

Desktop app

Reproduction or evidence

复现步骤

  1. 配置通道 A(OpenAI/Codex,任意可用凭据与额度),在会话里用 Codex 模型完成至少一轮带工具调用的对话;
  2. 确认历史中已产生加密/签名推理项(该通道的正常产物);
  3. 配置通道 B(另一供应商或另一套凭据/网关),确保其模型可用;
  4. 在同一会话中把模型切换到通道 B 的模型,发送任意一条消息;
  5. 观察:请求未发出,返回上述错误;
  6. 对照实验:把模型切回通道 A,同一会话可正常继续;另起新会话选通道 B,也可正常工作。

待确认(本机未能验证,因通道 A 额度已耗尽):同一通道内换模型(同凭据、同 endpoint)是否同样被拦截。若不被拦截,说明判定依据是凭据身份而非模型名,这一点建议在文档中写明。

Environment, if relevant

No response

主要语言
Rust
星标
2.3k
派生
236
平均合并
8 小时 1 分钟
30 天内合并 PR
228

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

GCWing/OpenBitFun 的其他 Issue

查看 GCWing/OpenBitFun 的全部 Issue

相似的 Issue

更多 Rust Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。