Commit Graph
3151 Commits
Author SHA1 Message Date
xlplbo 75fb3c41c2 fix(apicompat): responses→chat 桥支持 custom 工具,修复 Codex exec 丢失
Codex 0.14x 的核心执行工具 exec 是 type=custom(freeform)工具,
responsesToolsToChatTools 丢弃所有非 function 工具,导致模型工具列表中
没有 exec、无法执行任何命令;SSE 序列化层 responsesItemWire 也缺少
custom_tool_call 分支,即使产出该类型 item,call_id/name/input 也会被
白名单剥离。

- custom 工具降级为单一 input:string 参数的 function 工具
- 回程(非流式+流式)按名字还原 custom_tool_call 项/事件
- 历史 custom_tool_call / custom_tool_call_output 项转换(此前被跳过)
- responsesItemWire 补 custom_tool_call 分支
- tools 全部被丢弃时不再转发 tool_choice(修复上游 400
  "'tool_choice' is only allowed when 'tools' are specified")

feat(apicompat): chat 桥补齐 tool_search/namespace/字符串工具声明

对齐参考实现(cc-switch transform_codex_chat.rs)的工具类型覆盖:
- tool_search → 固定 function 代理工具(query 必填/limit 可选),
  历史项 tool_search_call/tool_search_output 一并转换
- namespace → 子 function 工具摊平为 <namespace>__<name>,
  超 64 字符截断加 sha256 短哈希;历史项带 namespace 字段时同规则摊平
- tools: ["name"] 字符串简写按 custom 工具解析

fix(apicompat): 工具调用宣告延迟到类型可判定,兼容 name 迟到的上游

output_item.added 的 custom/function 判定依赖工具名。名字晚于首个分片
到达时,此前会按 function_call 宣告造成 added/done 项类型不一致。
改为存在 custom 工具且名字未到时延迟宣告,宣告时补发已累积的参数增量;
请求无 custom 工具时行为不变。

PR 标题与正文(可直接粘贴)

标题:fix(apicompat): responses→chat 桥丢弃 custom 工具导致 Codex 无法执行命令

正文:

## 问题

OpenAI APIKey 账号接入仅支持 /v1/chat/completions 的第三方上游
(DeepSeek/GLM/Kimi 等)时,Codex 0.14x 客户端文本对话正常,但无法执行
任何命令:exec 调用全部 "aborted",spawn_agent 派出的子智能体永远卡在
running,多轮后模型自述"环境无法执行文件系统命令"放弃。

## 根因

Codex 0.14x 的核心执行工具 exec 是 Responses API 的 custom(freeform)
工具,而非普通 function:

    {"type": "custom", "name": "exec", "format": {"type": "grammar", ...}}

两处缺陷叠加:

1. `responsesToolsToChatTools` 丢弃所有非 function 工具
   (chatcompletions_responses_bridge.go),模型工具列表中从未出现 exec;
2. SSE 序列化 `responsesItemWire` 按 item 类型白名单重组字段,无
   custom_tool_call 分支,call_id/name/input 会被剥离
   (responses_stream_event_wire.go)。

次生缺陷:工具全部被丢弃时 tool_choice 仍被转发,部分上游报 400
"'tool_choice' is only allowed when 'tools' are specified"。

## 改动

(三个提交的要点清单,见各提交说明)

设计与 cc-switch 的 Codex chat 转换层(transform_codex_chat.rs /
streaming_codex_chat.rs)对齐:custom 工具降级为 {"input": string}
function 工具;custom 调用的参数增量不可流式还原为自由文本,缓冲至
收尾一次性下发 custom_tool_call_input.delta/done。

## 测试

- 新增 17 个单元测试:请求工具转换、tool_choice 兜底、历史项转换、
  非流式/流式回程还原、wire 序列化字段完整性、name 迟到宣告、命名摊平
- `go test ./internal/pkg/apicompat/...` 与 service 相关测试全绿
- 真机验证:Codex CLI 0.142.5 → 本桥 → GLM/DeepSeek 系 chat-only 上游,
  exec 实际执行命令并多轮续作正常

## 影响面

仅影响 "APIKey 账号 + 判定不支持 Responses" 的 chat 直转路径:

- OAuth 账号与透传路径不经过本桥(透传为字节级原文转发)
- CC→Responses 反方向仅构造 ResponsesRequest,新增字段带 omitempty
  且不赋值,序列化输出不变
- 探测逻辑未动,路由判定不变
- 共享类型改动均为增量容错(新字段 omitempty;UnmarshalJSON 只新增
  字符串工具的解析成功路径)
2026-07-07 16:23:44 +08:00
shaw 7c8e13208b style: replace interface{} with any per golangci-lint gofmt rewrite rules 2026-07-07 09:32:15 +08:00
shaw d37d216f9d Merge remote-tracking branch 'origin/main' into pr-3762
# Conflicts:
#	backend/internal/repository/concurrency_cache.go
#	backend/internal/repository/concurrency_cache_integration_test.go
2026-07-07 09:22:44 +08:00
Wesley Liddick a862eac1d8 Merge pull request #3758 from catoncat/fix/test-oauth-codex-headers
fix: add missing Codex CLI headers for OAuth account test
2026-07-07 08:34:18 +08:00
Wesley Liddick 3828a48752 Merge pull request #3751 from heathermhuang/codex/grok-image-pricing-latest
fix: expose Grok image pricing controls
2026-07-07 08:29:01 +08:00
Wesley Liddick 77b1a0452e Merge pull request #3760 from DaydreamCoding/fix/codex-detect-version-gate-message
fix(codex-detect): codex 版本门拒绝返回带版本号的差异化文案
2026-07-07 08:28:51 +08:00
Wesley Liddick 26b51ccfd3 Merge pull request #3737 from wucm667/fix/sync-models-non-v1-base-url
fix(models): 同步上游模型支持非 /v1 API 版本 base_url(如智谱 /v4)
2026-07-07 08:28:41 +08:00
cat cb151e36eb fix: respect custom User-Agent in OAuth account test
Match the image OAuth test path and gateway behavior: check for a
custom user_agent credential first, fall back to codexCLIUserAgent
only when no custom UA is configured.
2026-07-06 20:45:00 +08:00
jjaw 5089c303e1 优化 Redis SCAN 清理架构 2026-07-06 20:27:13 +08:00
shaw 31b6e0d94a fix: 请求头覆写审计问题修复(禁止名单缺口/beta 对称性/批量清空防护)
后端:
- 禁止名单补充 content-type(multipart boundary 为每请求随机值,静态覆写必坏
  images 路径)、x-claude-code-session-id / x-client-request-id(会击穿每请求
  会话同步,与已禁的 session_id 等同类)、cookie / x-goog-api-key(与透传路径
  的入站鉴权残留清洗保持一致)
- anthropic-beta 覆写与 body 净化对称:四个 Anthropic 构建器在
  sanitizeAnthropicBodyForBetaTokens 前以覆写值为有效 finalBeta,避免覆写
  丢 token 后 header/body 不对称被上游 400
- 保存/应用两路径共用 normalizeHeaderOverrideEntry,消除双份校验规则漂移
- GetHeaderOverrides 按 modelMappingCache 先例增加热路径缓存(WS 每消息
  重建头场景收益最大);ApplyHeaderOverrides 移除无观测效果的排序

前端:
- 禁止名单镜像同步新增项;值长度改按 UTF-8 字节校验(与后端 len() 对齐)
- Create 弹窗切换平台时重置覆写配置,避免上一平台模板行串台
- BulkEdit:开启覆写但无有效行时拦截保存(防止整键替换静默清空所选账号
  既有配置);开启分支显示整体替换警告;混合平台选择时隐藏模板按钮
- 删除未使用的 isBlockedHeaderOverrideName 导出
2026-07-06 20:05:50 +08:00
shaw ec7b20649e feat: apikey 账号支持请求头覆写(Anthropic/OpenAI)
- 账号 credentials 新增 header_override_enabled / header_overrides,
  仅对 anthropic/openai 平台的 api_key 账号生效
- 转发前对同名请求头做大小写不敏感覆盖(EqualFold 全量删除后按
  wire casing 写入单值),值为空的条目视为占位不参与覆盖
- 覆盖全部出站路径:/v1/messages(标准+透传)、count_tokens、
  /v1/responses、chat completions、embeddings、images、全部 WS 模式、
  账号测试/探测、上游模型列表
- 创建/编辑/批量更新统一校验:RFC 7230 名称格式、去重、长度/条目上限、
  24 个禁止覆写头(认证/连接控制/accept-encoding/sec-websocket-*/
  会话隔离头),应用时二次防御过滤
- 前端三个账号弹窗新增开关+键值行编辑器+模板按钮(Claude Code CLI /
  Codex CLI 标准头,值为空),本地校验与后端规则对齐,i18n 中英文
2026-07-06 19:11:08 +08:00
cat 1c0ccb477a fix: add missing Codex CLI headers for OAuth account test
The testOpenAIAccountConnection function was sending requests to
chatgpt.com/backend-api/codex/responses without the required Codex CLI
identification headers, causing 401 Unauthorized for OAuth/PAT accounts.

The gateway Forward path and the compact test path both set these headers:
- Originator: codex_cli_rs
- User-Agent: codex_cli_rs/<version>
- OpenAI-Beta: responses=experimental

This patch adds the same headers to the normal (non-compact) OAuth test
path. API key accounts and other platforms are unaffected since the
headers are inside the isOAuth branch.
2026-07-06 19:09:26 +08:00
DaydreamCodingandClaude Opus 4.8 4b321142bb fix(codex-detect): codex 版本门拒绝返回带版本号的差异化文案
codex_cli_only 的 403 出口此前对所有拒绝原因硬编码同一句
"This account only allows Codex official clients",但能走到版本门
(判定链第 5 步)的请求其实已命中官方 UA/originator,仅版本不符,
再回「只允许官方客户端」会误导。

- CodexClientRestrictionDetectionResult 增 DetectedVersion/MinCodexVersion/
  MaxCodexVersion,仅在 VersionTooLow/VersionTooHigh 分支填充
- 新增 CodexClientRestrictionMessage:too_low/too_high 给出带实际版本号与
  边界的提示;undetectable/未命中/黑名单/缺指纹 仍用通用兜底句(不泄露门控细节)
- Forward 唯一 403 出口改用该映射函数

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 19:03:36 +08:00
Heatherm Huang de31e57333 test: document Grok video media billing unit 2026-07-06 18:32:29 +08:00
Heatherm Huang 95717a7e62 test: clarify Grok video billing test name 2026-07-06 18:30:32 +08:00
Heatherm Huang 6b4c3c6ed1 test: cover Grok video media rate billing 2026-07-06 18:23:38 +08:00
wucm667 f881ff7cb0 fix(models): support non-v1 OpenAI models URLs 2026-07-06 17:46:44 +08:00
shaw 6cea1c35bb feat: 适配 OpenAI 新模型 gpt-5.6-sol/terra/luna 2026-07-06 17:34:22 +08:00
Wesley Liddick d1d3400b69 Merge pull request #3645 from bestony/worktree/lucky-harbor-dbe4
feat(keys): add api key concurrency stats
2026-07-06 16:45:15 +08:00
github-actions[bot] 76bb7b0338 chore: sync VERSION to 0.1.145 [skip ci] 2026-07-06 08:30:51 +00:00
Albert Coady 0dc6e56aae fix: harden easypay custom method validation 2026-07-06 15:00:29 +08:00
Albert Coady bf76168ba5 feat: add custom easypay payment methods 2026-07-06 15:00:29 +08:00
shaw d56e94b875 feat(payment): 订阅 CNY 换算改为独立汇率配置的显式 opt-in
- 新增 SUBSCRIPTION_USD_TO_CNY_RATE 配置(1 USD = X CNY,默认 0=关闭),
  替代复用 balance_recharge_multiplier 的隐式换算,促销倍率与订阅定价解耦
- 未配置汇率时订阅保持 price 直付的存量行为,存量部署升级零影响
- 前端确认页/原价/手续费/方式限额与后端换算条件严格镜像(rate>0 且币种为 CNY)
- 管理后台新增汇率配置输入(zh/en 文案),checkout-info 透出 subscription_usd_to_cny_rate
- 单测锁定:汇率未配置时不换算、换算使用汇率而非余额倍率、余额订单不受影响、返利仍按 USD price
2026-07-06 14:34:17 +08:00
shaw d089c5789c Merge branch 'pr-3738' into fix/subscription-cny-optin-rate 2026-07-06 14:14:19 +08:00
shaw 0fd2e9216d fix(scheduler): 修复 OpenAI 高级调度器审计发现的正确性与性能问题
针对 #3692 合并后审计发现的问题集中修复:

- previous_response_id 剥离条件改为按 call_id 全覆盖校验,
  部分可重建的工具续链不再被误剥离(不受开关门控的行为回归)
- 粘性加权回退路径补分组归属校验并清理失效绑定,杜绝跨分组账号泄漏
- 账号列表页:无 OpenAI 账号时跳过分数计算、过滤池限定 openai 平台、
  负载批查合并为账号并集一次查询,消除全表扫描与 Redis N+1
- 订阅优先模式下常规池不可用时回退订阅池等待计划,
  busy-but-waitable 的订阅账号不再导致请求硬失败
- TopK/权重 DB 覆盖显式受总开关门控,与兄弟子开关语义一致
- 前端未分组 OpenAI 账号回退展示基础分,不再显示 "-"
- ListAllWithFilters 等能力正式进入 AccountRepository/AdminService 接口,
  移除匿名接口断言与静默降级;负载批查失败补 warn 日志
- SelectAccountWithSchedulerForCapability 增加显式 previousResponseCanMove
  参数,移除 "previous_response_can_move" 魔法字符串哨兵
- 设置写入路径补"基础权重不得全为零"聚合校验;
  运行时设置批量读取失败的降级路径覆盖全部键并留痕
2026-07-06 11:43:16 +08:00
Wesley Liddick 759332aa92 Merge pull request #3692 from linshuboy/codex/openai-advanced-scheduler-controls-squashed
[codex] add OpenAI advanced scheduler controls
2026-07-06 10:59:23 +08:00
wucm667 b408edf97b fix(payment): convert subscription CNY pay amount 2026-07-06 10:56:43 +08:00
Wesley Liddick f0f1f59a44 Merge pull request #3716 from wucm667/fix/antigravity-refresh-server-invalidated-token
fix(antigravity): 401 服务端失效的 token 触发主动刷新,修复 NeedsRefresh 仅看 expires_at 的死循环
2026-07-06 10:18:49 +08:00
Wesley Liddick ac68d73f0c Merge pull request #3717 from wucm667/fix/anthropic-models-honor-group-config
fix(gateway): Anthropic /v1/models 返回分组 models_list_config 自定义模型列表
2026-07-06 10:18:36 +08:00
linshuboy f26ca5661e feat: add OpenAI advanced scheduler controls
Related: #1089, #408, #123
2026-07-05 17:24:38 +08:00
wucm667 41cdd438d7 fix(gateway): honor Anthropic custom models list 2026-07-05 08:35:34 +08:00
wucm667 b23475ac0c fix(antigravity): refresh server-invalidated tokens 2026-07-05 08:34:34 +08:00
MoRanHuiShou-pug e76e0499d7 fix: sanitize payment response NUL bytes 2026-07-05 03:55:40 +08:00
github-actions[bot] b650bdd68d chore: sync VERSION to 0.1.144 [skip ci] 2026-07-04 03:49:44 +00:00
Wesley Liddick 41def4ba03 Merge pull request #3699 from wucm667/fix/responses-billing-use-mapped-model
fix(openai): /v1/responses 计费使用映射后模型,修复按原始模型价格误计费
2026-07-04 10:40:53 +08:00
Wesley Liddick 707b87a96f Merge pull request #3676 from wucm667/fix/codex-import-refresh-token-missing-collision
fix(admin): Codex Session 导入 refresh_token 缺失时不再合并同 workspace 不同账号
2026-07-04 10:29:47 +08:00
wucm667 4dd3aee5c2 fix(openai): use mapped billing model for responses 2026-07-04 10:04:42 +08:00
wucm667 6bd248fd1f fix(admin): avoid merging Codex access-only imports 2026-07-04 09:53:01 +08:00
Wesley Liddick dfdb838ce4 Merge pull request #3690 from AndersonBY/feature/codex-image-tool-policy
feat: add Codex image tool strip policy
2026-07-04 09:27:37 +08:00
Wesley Liddick be3aee1a4b Merge pull request #3694 from zhaoteng45/fix/antigravity-gemini31-pro-agent
fix: normalize Antigravity Gemini 3.1 Pro routing
2026-07-04 09:23:46 +08:00
Wesley Liddick 588e02b29e Merge pull request #3673 from heathermhuang/codex/grok-admin-issues-3545-3649
fix: preserve configured Grok OAuth concurrency
2026-07-04 09:22:18 +08:00
Wesley Liddick 659086861d Merge pull request #3670 from weiness/feat/configurable-migration-timeout
feat: make setup migration timeout configurable
2026-07-04 09:21:14 +08:00
Wesley Liddick 0c53d4f334 Merge pull request #3661 from jianjianai/fix/group-capacity-batch-hotpath
fix: 优化用户组容量汇总批量统计性能
2026-07-04 09:20:02 +08:00
Wesley Liddick 8e07ec55db Merge pull request #3685 from jianjianai/fix/optimize-slot-cleanup
fix: 优化并发槽位定时清理性能
2026-07-04 09:19:34 +08:00
Wesley Liddick 129d05258a Merge pull request #3696 from jianjianai/codex/token-expired-nonretryable
fix:添加 token_expired 刷新不可重试判断
2026-07-04 09:18:43 +08:00
Wesley Liddick 46ef146225 Merge pull request #3689 from DaydreamCoding/feat/usage-error-requests-alignment
feat(usage): 错误请求对齐用量明细(UI/排序/筛选/列设置)
2026-07-04 09:18:08 +08:00
shaw b3f796972e feat(anthropic): treat 7d_oi (Fable) window 429 as model-level rate limit
Anthropic OAuth/setup-token accounts now expose a Fable-only 7d window in
429 responses (anthropic-ratelimit-unified-7d_oi-*). Previously, a 429
triggered solely by this window fell through to the legacy path and marked
the whole account rate limited (picking the sooner 5h/7d reset) even though
5h/7d were still "allowed".

Backend:
- HandleUpstreamError: when only 7d_oi is rejected/exceeded, set a
  model-level rate limit (scope "claude-fable-5") and skip account-level
  SetRateLimited / session-window rewrite / temp-unsched entirely.
  Real 5h/7d exhaustion keeps the exact legacy account-level behavior.
- modelRateLimitKeysForRequest: append the Fable family scope for any
  mapped Anthropic model containing "fable", so claude-fable-5[1m] and
  other variants are excluded from scheduling together.
- Passive sampling: collect 7d_oi utilization/reset into Extra
  (passive_usage_7d_oi_*), including from the 429 response itself so the
  usage bar doesn't freeze at a stale value during the limit window;
  cleared on 5h window reset like the existing 7d keys.
- Usage API: new seven_day_fable window in UsageInfo. GetPassiveUsage
  builds it from passive samples; active queries parse the upstream
  seven_day_overage_included field (claim name from the
  representative-claim header) and fall back to passive data when the
  upstream usage API doesn't return it, so the active-query button keeps
  the bar populated. seven_day_sonnet handling is preserved unchanged.

Frontend:
- /admin/accounts usage cell renders a "7d F" (amber) progress bar next to
  the existing 5h / 7d / 7d S bars for Anthropic OAuth/setup-token accounts.
2026-07-04 08:28:30 +08:00
jjaw e5dc1f5972 添加 token_expired 刷新不可重试判断 2026-07-04 04:30:15 +08:00
zhaoteng45 df2cedeead fix: normalize antigravity gemini 3.1 pro routing 2026-07-03 17:22:49 +00:00
DaydreamCoding ebbdc70311 feat(usage): 错误请求对齐用量明细(UI/排序/筛选/列设置)
错误请求列表(/admin/usage 错误 tab、Ops 弹窗、用户端 /usage 错误 tab)
对齐用量明细的交互与信息密度。Squash of:

- feat(usage): 错误请求全面对齐用量明细(UI/列序/排序/筛选/列设置/新列)
- refactor(usage): 错误表提取共享徽章工具与 IP 批量工具条,后端排序解析归并 SetSort
- feat(usage): /admin/usage 错误请求新增分类过滤
- fix(ops): 错误列表 phase=upstream 过滤生效,守卫豁免改为显式 opt-in
- fix(ops): 错误列表用户列回退显示已删除 KEY 所有者
- fix(usage): 错误请求状态码排序对齐展示/过滤,筛选项改固定常用码
2026-07-03 23:02:27 +08:00