Skip to content

feat: 集成 CLIProxyAPI 作为 Antigravity/Codex 的可选转发后端#186

Merged
dreamhunter2333 merged 4 commits intomainfrom
feat/cliproxyapi-integration
Feb 6, 2026
Merged

feat: 集成 CLIProxyAPI 作为 Antigravity/Codex 的可选转发后端#186
dreamhunter2333 merged 4 commits intomainfrom
feat/cliproxyapi-integration

Conversation

@dreamhunter2333
Copy link
Member

@dreamhunter2333 dreamhunter2333 commented Feb 6, 2026

在原生 Antigravity 和 Codex provider 配置中新增 useCLIProxyAPI 开关, 启用后 NewAdapter() 直接委托给 CLIProxyAPI adapter 处理请求,无需独立 的 provider 类型。前端在创建页和编辑页均可控制此开关,编辑页支持
即时切换并自动保存(含失败回滚)。

Summary by CodeRabbit

  • 新功能

    • 为 Antigravity 与 Codex 提供商新增可切换的 “Use CLIProxyAPI” 选项,支持在创建与编辑时保存并生效。
    • 在提供商详情与导入流程新增统一的可访问性开关组件,支持即时切换与乐观更新回滚。
  • 本地化

    • 增加对应中文/英文文案条目以支持新开关文本显示。
  • 依赖更新

    • 对若干底层依赖版本进行了更新与对齐以提升兼容性与稳定性。

在原生 Antigravity 和 Codex provider 配置中新增 useCLIProxyAPI 开关,
启用后 NewAdapter() 直接委托给 CLIProxyAPI adapter 处理请求,无需独立
的 provider 类型。前端在创建页和编辑页均可控制此开关,编辑页支持
即时切换并自动保存(含失败回滚)。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

为 Antigravity 与 Codex 提供商引入可选的 CLIProxyAPI 转发:新增两个 CLIProxyAPI 适配器、后端域模型与配置扩展、前端开关与 UI,且更新 go.mod 添加/替换相关依赖与 replace 指令。

Changes

Cohort / File(s) Summary
依赖管理
go.mod
新增/升级多项依赖;加入 github.com/router-for-me/CLIProxyAPI/v6 要求并用 replace 指向 github.com/awsl-project/CLIProxyAPI/v6 的内部引用;调整和提升多项间接依赖版本。
Antigravity 适配器
internal/adapter/provider/antigravity/adapter.go, internal/adapter/provider/cliproxyapi_antigravity/adapter.go
在原 Antigravity 初始化中加入当 useCLIProxyAPI 为 true 时的早期返回,直接委派到新增的 CLIProxyAPIAntigravityAdapter;新增适配器实现支持流/非流执行、模型替换、鉴权、事件上报与错误映射。
Codex 适配器
internal/adapter/provider/codex/adapter.go, internal/adapter/provider/cliproxyapi_codex/adapter.go
在 Codex 初始化中加入同样的 useCLIProxyAPI 委派路径;新增 CLIProxyAPICodex 适配器,包含流/非流执行、令牌使用计算、模型提取与事件上报逻辑。
域模型与配置
internal/domain/model.go
新增 ProviderConfigCLIProxyAPIAntigravityProviderConfigCLIProxyAPICodex 配置结构,并在 ProviderConfig 中以 json:"-" 字段引入 CLIProxyAPI 子配置;在 Antigravity 与 Codex 配置中加入 useCLIProxyAPI 布尔标志。
前端类型
web/src/lib/transport/types.ts
ProviderConfigAntigravityProviderConfigCodex 接口中新增可选字段 useCLIProxyAPI,以支持 UI 传输该标志。
前端 UI 组件
web/src/pages/providers/components/cliproxyapi-switch.tsx
新增受控开关组件 CLIProxyAPISwitch(props: checked, onChange, disabled),带可访问性属性与图标。
Antigravity 提供商视图与导入
web/src/pages/providers/components/antigravity-provider-view.tsx, web/src/pages/providers/components/antigravity-token-import.tsx
添加 useCLIProxyAPI 本地状态、开关 UI、乐观更新并通过 updateProvider 持久化,导入流程/OAuth 完成路径中也包含该标志。
Codex 提供商视图与导入
web/src/pages/providers/components/codex-provider-view.tsx, web/src/pages/providers/components/codex-token-import.tsx
同样添加 useCLIProxyAPI 状态与 UI 开关、响应式布局调整及持久化更新逻辑,并在创建/导入请求中传递该字段。
本地化
web/src/locales/en.json, web/src/locales/zh.json
新增 useCLIProxyAPIcliProxyAPI 的本地化条目以支持新的 UI 文案。

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FrontEnd as 前端(UI)
    participant Backend as 后端
    participant CLIProxyAPI as CLIProxyAPI适配器
    participant Executor as 执行器

    User->>FrontEnd: 在提供商设置启用 useCLIProxyAPI
    FrontEnd->>Backend: PATCH 提供商配置 (useCLIProxyAPI=true)
    Backend-->>FrontEnd: 200 OK

    Note over User,Backend: 后续请求处理流程
    User->>FrontEnd: 发送 AI 请求
    FrontEnd->>Backend: 转发请求
    Backend->>Backend: 检查 provider.config.useCLIProxyAPI
    alt useCLIProxyAPI = true
        Backend->>CLIProxyAPI: 构建并调用 Adapter.Execute (流/非流)
        CLIProxyAPI->>Executor: 构造并执行请求
        Executor-->>CLIProxyAPI: 返回响应或流事件
        CLIProxyAPI->>Backend: 返回处理后的响应并上报事件/指标
    else
        Backend->>Executor: 使用原始适配器/执行器直接执行
        Executor-->>Backend: 返回响应
    end
    Backend-->>FrontEnd: 返回最终响应
    FrontEnd-->>User: 呈现结果
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • whhjdi

Poem

🐰 我在草丛轻敲键盘声,
开关一按云端换新程,
适配器短跑转委派,
流与非流同舞轻盈,
别忘汇报令牌与心情。

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确描述了PR的主要变更:在Antigravity和Codex提供商中集成CLIProxyAPI作为可选的转发后端,涵盖了核心功能改进。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/cliproxyapi-integration

🧹 Recent nitpick comments
web/src/pages/providers/components/codex-provider-view.tsx (1)

352-383: 之前的 review 问题已全部修复。

  • config 空值检查已移至乐观更新之前(Line 363)✓
  • 硬编码标签已替换为 t('providers.cliProxyAPI')(Line 538)✓

CLIProxyAPI 切换逻辑(state + useEffect + handler)与 antigravity-provider-view.tsx(Lines 278-310)几乎完全相同,仅 config 路径不同(codex vs antigravity)。可以考虑抽取为自定义 hook 以消除重复。

♻️ 可选:抽取自定义 hook 示例
// hooks/use-cliproxyapi-toggle.ts
function useCLIProxyAPIToggle(
  provider: Provider,
  configKey: 'antigravity' | 'codex',
) {
  const updateProvider = useUpdateProvider();
  const configValue = provider.config?.[configKey]?.useCLIProxyAPI ?? false;
  const [checked, setChecked] = useState(() => configValue);

  useEffect(() => { setChecked(configValue); }, [configValue]);

  const toggle = async (next: boolean) => {
    const sectionConfig = provider.config?.[configKey];
    if (!sectionConfig) return;
    const prev = checked;
    setChecked(next);
    try {
      await updateProvider.mutateAsync({
        id: provider.id,
        data: {
          ...provider,
          config: {
            ...provider.config,
            [configKey]: { ...sectionConfig, useCLIProxyAPI: next },
          },
        },
      });
    } catch {
      setChecked(prev);
    }
  };

  return { checked, toggle, isPending: updateProvider.isPending };
}
📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31f3d2c and 7cbdabb.

📒 Files selected for processing (4)
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/providers/components/antigravity-provider-view.tsx
  • web/src/pages/providers/components/codex-provider-view.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
web/src/pages/providers/components/antigravity-provider-view.tsx (3)
web/src/lib/transport/http-transport.ts (1)
  • updateProvider (113-116)
web/src/hooks/queries/use-providers.ts (1)
  • useUpdateProvider (50-62)
web/src/pages/providers/components/cliproxyapi-switch.tsx (1)
  • CLIProxyAPISwitch (10-40)
web/src/pages/providers/components/codex-provider-view.tsx (3)
web/src/lib/transport/http-transport.ts (1)
  • updateProvider (113-116)
web/src/hooks/queries/use-providers.ts (1)
  • useUpdateProvider (50-62)
web/src/pages/providers/components/cliproxyapi-switch.tsx (1)
  • CLIProxyAPISwitch (10-40)
🔇 Additional comments (5)
web/src/locales/zh.json (1)

58-59: LGTM!新增的 i18n 键值与 en.json 保持一致。

common.backcommon.useCLIProxyAPIproviders.cliProxyAPI 的中文翻译准确且放置在正确的命名空间下。

Also applies to: 275-275

web/src/locales/en.json (1)

58-59: LGTM!

新增的本地化键与 zh.json 结构一致,英文翻译准确。

Also applies to: 275-275

web/src/pages/providers/components/antigravity-provider-view.tsx (2)

288-310: 之前的 review 问题已修复,乐观更新逻辑正确。

config 检查已移至 setUseCLIProxyAPI(checked) 之前(Line 289-290),disabled={updateProvider.isPending} 防止了并发切换的竞态问题。实现整体合理。

一个小建议:catch 块目前仅静默回滚 UI 状态,用户不会收到任何失败反馈。考虑添加 toast 通知以提升 UX。


428-437: CLIProxyAPI 开关在 Antigravity 视图中始终可见,与 Codex 视图行为不一致。

此处 CLIProxyAPISwitch 位于 info card grid 中,不受 refreshToken 条件控制,始终渲染。而 codex-provider-view.tsx 中(Line 513),整个包含 CLIProxyAPI 开关的区块被包裹在 {config?.refreshToken && (...)} 内,仅在有 refreshToken 时显示。

如果这是有意为之(Antigravity provider 始终有 token)可以忽略,否则建议统一两个视图的条件渲染逻辑。

web/src/pages/providers/components/codex-provider-view.tsx (1)

514-545: 刷新令牌区域重构为双栏布局,结构清晰。

i18n 调用和 CLIProxyAPISwitch 集成均正确。disabled={updateProvider.isPending} 确保更新期间禁用交互。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In `@internal/adapter/provider/cliproxyapi_antigravity/adapter.go`:
- Around line 66-70: The error returned from updateModelInBody should not be
marked retryable because JSON parse failures won't be fixed by retrying; update
the error construction where updateModelInBody is handled (currently calling
domain.NewProxyErrorWithMessage) to pass retryable=false instead of true and
keep the existing error message (e.g., "failed to update model in body: %v") so
callers receive a non-retryable proxy error for JSON parse/update failures.
- Around line 170-184: The stream loop currently swallows chunk errors (if
chunk.Err != nil it only logs and breaks) and always returns nil, so change the
handler in the loop inside ExecuteStream's consumer (the for chunk := range
stream block) to return a domain.NewProxyErrorWithMessage (or equivalent used by
other adapters) when chunk.Err is non-nil so the caller can observe the failure;
also stop unconditionally appending an extra '\n' after writing chunk.Payload to
w and to sseBuffer — instead, inspect/assume the payload format from
executor.ExecuteStream and only add terminators if payloads do not already
include the SSE delimiter (used by usage.ExtractFromStreamContent and
sseBuffer), ensuring w.Write/ flusher.Flush behavior remains unchanged.

In `@internal/adapter/provider/cliproxyapi_codex/adapter.go`:
- Around line 134-148: The loop over stream currently breaks on chunk.Err but
always returns nil, so callers can't detect stream failures; inside the for
chunk := range stream loop (referencing chunk.Err, sseBuffer, w.Write and
flusher.Flush), when chunk.Err != nil you should: record the error, and if no
data was successfully written yet (e.g., sseBuffer.Len() == 0 or track a
bytesWritten flag) return that error to the caller; if some payload was already
sent, break normally and return nil (or a wrapped warning) so partial data is
preserved. Update the error branch to check the buffer/bytesWritten and return
chunk.Err when appropriate instead of unconditionally breaking.
- Around line 89-112: In executeNonStream, after extracting usage metrics you
must also emit the missing response info and response model events and include
cache metrics in AdapterMetrics: call extractModelFromResponse(resp.Payload) and
if non-nil call eventChan.SendResponseModel with the model, call
eventChan.SendResponseInfo similar to codex/adapter.go, and when sending
AdapterMetrics include cache-related fields from metrics (e.g., CacheReadCount,
CacheMissCount/CacheHitCount, CachedTokens) in addition to InputTokens and
OutputTokens so CLIProxyAPICodexAdapter matches the observability behavior of
the original codex adapter.
- Around line 114-162: The streaming path in executeStream is missing TTFT
reporting; detect the time of the first non-empty chunk inside the for loop that
iterates over stream (after verifying ctxutil.GetEventChan(ctx) != nil), compute
elapsed time since request start (capture start time at function entry or just
before reading the loop), and call eventChan.SendFirstToken(elapsedMillis) the
first time you write a non-empty chunk to the ResponseWriter (ensure this runs
only once). Use the existing symbols executeStream, stream, chunk.Payload,
flusher, ctxutil.GetEventChan, and eventChan.SendFirstToken to locate where to
add this logic and guard the call so it does not change existing behavior when
eventChan is nil.

In `@web/src/pages/providers/components/antigravity-provider-view.tsx`:
- Around line 288-310: The optimistic state update in handleToggleCLIProxyAPI is
done before validating provider.config.antigravity, so if antigravityConfig is
missing the function returns without rolling back; change the flow to either
check for antigravityConfig before calling setUseCLIProxyAPI or, if keeping the
optimistic update, ensure you revert to prev (captured from useCLIProxyAPI)
before any early return; update the function around useCLIProxyAPI,
setUseCLIProxyAPI, the antigravityConfig check and the
updateProvider.mutateAsync call to perform the config existence check first or
to call setUseCLIProxyAPI(prev) before returning.

In `@web/src/pages/providers/components/cliproxyapi-switch.tsx`:
- Around line 21-36: The switch button (the element using role="switch" and
props checked/disabled/onChange) needs a visible keyboard focus indicator and a
label association: add focus-visible classes (e.g. focus-visible:outline-none
focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary) to
the button's className so keyboard users see focus, and add an aria-labelledby
(or aria-label) to the same button to point to the descriptive label element
next to the switch (or provide a clear aria-label fallback) so the role="switch"
with aria-checked correctly associates with its visible text label; keep the
existing checked/disabled/onChange behavior.

In `@web/src/pages/providers/components/codex-provider-view.tsx`:
- Around line 362-383: The optimistic update in handleToggleCLIProxyAPI calls
setUseCLIProxyAPI(checked) but returns early when config is falsy, leaving the
UI inconsistent; change the flow to either check config before doing the
optimistic set or, if you keep the optimistic set, explicitly revert by calling
setUseCLIProxyAPI(prev) when config is missing (i.e., inside the if (!config)
branch) so the UI rolls back; update the handler around the useCLIProxyAPI,
setUseCLIProxyAPI, config and updateProvider.mutateAsync usage accordingly.
🧹 Nitpick comments (7)
internal/domain/model.go (1)

130-160: CLIProxyAPI 配置结构体的 json tag 可能产生误导。

ProviderConfigCLIProxyAPIAntigravityProviderConfigCLIProxyAPICodex 的字段带有 json tag,但它们通过 ProviderConfig 中的 json:"-" 标记永远不会被序列化。如果这些 json tag 仅用于文档目的而非实际序列化,建议添加注释说明;如果确实有其他序列化场景(如日志或内部调试),则无需更改。

web/src/pages/providers/components/antigravity-token-import.tsx (1)

262-263: 考虑在异步操作期间禁用开关。

CLIProxyAPISwitchcreatingvalidating 状态下未被禁用,用户可以在提交过程中切换状态。虽然由于 React 闭包捕获机制,不会影响正在进行的请求,但可能造成 UX 上的困惑。

建议添加 disabled 属性
-          <CLIProxyAPISwitch checked={useCLIProxyAPI} onChange={setUseCLIProxyAPI} />
+          <CLIProxyAPISwitch checked={useCLIProxyAPI} onChange={setUseCLIProxyAPI} disabled={creating || validating} />
go.mod (1)

14-14: replace 指令旁添加注释,说明该模块为 fork 以及版本号差异的原因。

require 中声明的 v6.7.53replace 指向的伪版本 v6.0.0-20260205175451-880f8f06a282 存在差异。这是使用 fork 时的常见 Go modules 模式——require 版本号来自原始模块的期望,而 replace 指令将其重定向到实际的源码(在本例中是 fork 版本)。建议在第 107 行的 replace 指令旁添加注释,说明这是 fork 关系以及为什么版本号不同,以便后续维护者理解这个配置的意图。

此外,replace 指向的伪版本基于 2026-02-05 的提交,后续如果上游模块更新,需要手动同步此版本。

web/src/pages/providers/components/codex-provider-view.tsx (1)

536-539: 硬编码的 "CLIProxyAPI" 标签未使用 i18n。

第 538 行的 CLIProxyAPI 标签是硬编码文本,与文件中其他使用 t(...) 的文本不一致。建议使用翻译键保持一致性。

🌐 建议修复
                  <div className="text-xs text-muted-foreground uppercase tracking-wider font-semibold mb-1.5">
-                    CLIProxyAPI
+                    {t('common.useCLIProxyAPI', 'CLIProxyAPI')}
                  </div>
web/src/pages/providers/components/antigravity-provider-view.tsx (2)

428-437: 硬编码 "CLIProxyAPI" 标签,建议与 CLIProxyAPISwitch 组件内的翻译键保持一致。

第 430 行硬编码了 CLIProxyAPI,与组件其他部分使用 t(...) 的模式不一致。

🌐 建议修复
                 <div className="text-xs text-muted-foreground uppercase tracking-wider font-semibold mb-1.5">
-                  CLIProxyAPI
+                  {t('common.useCLIProxyAPI', 'CLIProxyAPI')}
                 </div>

278-310: handleToggleCLIProxyAPI 逻辑在 codex 和 antigravity 视图中高度重复。

此函数的逻辑(乐观更新、mutateAsync、rollback)与 codex-provider-view.tsx 中几乎完全相同,仅 config 路径不同。可考虑提取为共享的自定义 hook(如 useToggleCLIProxyAPI)以减少重复,但不阻塞合并。

internal/adapter/provider/cliproxyapi_antigravity/adapter.go (1)

111-119: updateModelInBody 通过反序列化再序列化会丢失原始 JSON 格式。

使用 map[string]interface{} 反序列化后重新 json.Marshal 会改变字段顺序,且可能改变数字精度(大整数)。对于大多数 API 场景这不是问题,但如果上游对 JSON 格式敏感需留意。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 039ae13 and fac48c6.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (12)
  • go.mod
  • internal/adapter/provider/antigravity/adapter.go
  • internal/adapter/provider/cliproxyapi_antigravity/adapter.go
  • internal/adapter/provider/cliproxyapi_codex/adapter.go
  • internal/adapter/provider/codex/adapter.go
  • internal/domain/model.go
  • web/src/lib/transport/types.ts
  • web/src/pages/providers/components/antigravity-provider-view.tsx
  • web/src/pages/providers/components/antigravity-token-import.tsx
  • web/src/pages/providers/components/cliproxyapi-switch.tsx
  • web/src/pages/providers/components/codex-provider-view.tsx
  • web/src/pages/providers/components/codex-token-import.tsx
🧰 Additional context used
🧬 Code graph analysis (7)
internal/domain/model.go (2)
web/src/lib/transport/types.ts (3)
  • ModelMapping (420-436)
  • ProviderConfigAntigravity (25-32)
  • ProviderConfigCodex (44-58)
internal/repository/sqlite/models.go (2)
  • ModelMapping (149-161)
  • ModelMapping (163-163)
web/src/pages/providers/components/antigravity-token-import.tsx (1)
web/src/pages/providers/components/cliproxyapi-switch.tsx (1)
  • CLIProxyAPISwitch (10-39)
web/src/pages/providers/components/antigravity-provider-view.tsx (4)
web/src/lib/transport/http-transport.ts (1)
  • updateProvider (113-116)
web/src/hooks/queries/index.ts (1)
  • useUpdateProvider (11-11)
web/src/hooks/queries/use-providers.ts (1)
  • useUpdateProvider (50-62)
web/src/pages/providers/components/cliproxyapi-switch.tsx (1)
  • CLIProxyAPISwitch (10-39)
web/src/pages/providers/components/codex-token-import.tsx (1)
web/src/pages/providers/components/cliproxyapi-switch.tsx (1)
  • CLIProxyAPISwitch (10-39)
internal/adapter/provider/codex/adapter.go (2)
internal/domain/model.go (4)
  • Provider (173-201)
  • ProviderConfig (162-170)
  • ProviderConfigCLIProxyAPICodex (151-160)
  • ModelMapping (753-778)
internal/adapter/provider/cliproxyapi_codex/adapter.go (1)
  • NewAdapter (26-47)
web/src/pages/providers/components/codex-provider-view.tsx (3)
web/src/lib/transport/http-transport.ts (1)
  • updateProvider (113-116)
web/src/hooks/queries/use-providers.ts (1)
  • useUpdateProvider (50-62)
web/src/pages/providers/components/cliproxyapi-switch.tsx (1)
  • CLIProxyAPISwitch (10-39)
internal/adapter/provider/cliproxyapi_antigravity/adapter.go (4)
internal/context/context.go (6)
  • GetClientType (82-87)
  • GetRequestBody (138-143)
  • GetIsStream (181-186)
  • GetRequestModel (110-115)
  • GetMappedModel (117-122)
  • GetEventChan (203-208)
internal/domain/errors.go (1)
  • NewProxyErrorWithMessage (61-63)
internal/usage/extractor.go (2)
  • ExtractFromResponse (31-49)
  • ExtractFromStreamContent (294-296)
internal/domain/adapter_event.go (1)
  • AdapterMetrics (20-27)
🔇 Additional comments (7)
web/src/lib/transport/types.ts (1)

31-31: LGTM!

新增的 useCLIProxyAPI 可选布尔字段与 Go 后端 json:"useCLIProxyAPI,omitempty" 保持一致,类型和可选性设置正确。

Also applies to: 57-57

internal/domain/model.go (1)

162-169: 运行时字段设计合理。

使用 json:"-" 标记内部运行时字段,确保 CLIProxyAPI 配置不会被序列化到 API 响应或数据库中,这是正确的设计。

internal/adapter/provider/codex/adapter.go (1)

54-72: CLIProxyAPI 委托路径实现清晰。

提前提取 config 变量并在 UseCLIProxyAPI 为 true 时立即委托给 CLIProxyAPI adapter,避免了不必要的 token cache 初始化和 HTTP client 创建。字段映射与 ProviderConfigCLIProxyAPICodex 结构体匹配。

internal/adapter/provider/antigravity/adapter.go (1)

45-64: CLIProxyAPI 委托路径与 Codex 版本保持一致,实现正确。

字段映射完整覆盖了 ProviderConfigCLIProxyAPIAntigravity 所需的所有字段(Email、RefreshToken、ProjectID、ModelMapping、HaikuTarget),正确排除了 Endpoint 字段(由 CLIProxyAPI 内部处理)。

web/src/pages/providers/components/antigravity-token-import.tsx (1)

185-185: 两条创建路径均正确传递了 useCLIProxyAPI 标志。

Token 手动导入路径(第 185 行)和 OAuth 路径(第 224 行)都将 useCLIProxyAPI 包含在创建请求的 payload 中,与后端 ProviderConfigAntigravityUseCLIProxyAPI 字段对齐。

Also applies to: 224-224

web/src/pages/providers/components/codex-token-import.tsx (1)

48-49: LGTM!CLIProxyAPI 开关集成清晰。

状态声明、在 OAuth 和 Token 两种创建流程中传递 useCLIProxyAPI、以及在模式选项卡上方渲染开关组件,逻辑一致且正确。开关位于模式选项卡之上,确保两种模式下都可见,这是合理的。

Also applies to: 252-252, 295-295, 335-337

internal/adapter/provider/cliproxyapi_antigravity/adapter.go (1)

27-51: 项目所有 adapter 已统一使用标准库 log.Printf,无需更改。

验证了解多个 adapter(custom、cliproxyapi_codex、cliproxyapi_antigravity、antigravity 等)均采用相同的 log.Printf 日志方式,logging 规范已保持一致,无需统一。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +170 to +184
for chunk := range stream {
if chunk.Err != nil {
log.Printf("[CLIProxyAPI-Antigravity] stream chunk error: %v", chunk.Err)
break
}
if len(chunk.Payload) > 0 {
// Collect for token extraction
sseBuffer.Write(chunk.Payload)
sseBuffer.WriteByte('\n')

_, _ = w.Write(chunk.Payload)
_, _ = w.Write([]byte("\n"))
flusher.Flush()
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check other adapters' executeStream implementation to confirm SSE chunk format
rg -n --type=go 'flusher\.Flush\(\)' -B5 -A2

Repository: awsl-project/maxx

Length of output: 7916


🏁 Script executed:

#!/bin/bash
# Read the full function context around lines 170-184
fd -e go 'adapter.go' -x cat -n {} | grep -A 40 -B 10 "for chunk := range stream"

Repository: awsl-project/maxx

Length of output: 3314


🏁 Script executed:

#!/bin/bash
# Check the ExecuteStream interface/function signature to understand what payload contains
rg -n --type=go 'ExecuteStream' -A 5 -B 2 | head -100

Repository: awsl-project/maxx

Length of output: 1588


🏁 Script executed:

#!/bin/bash
# Check if ExecuteStream or stream chunk documentation explains payload format
rg -n --type=go 'type.*Stream\|type.*Chunk' -A 3 internal/executor/ internal/domain/ | head -50

Repository: awsl-project/maxx

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check error handling patterns in other adapters to compare
rg -n --type=go 'if.*chunk\.Err.*!= nil' -A 2 -B 1

Repository: awsl-project/maxx

Length of output: 831


流式传输中 chunk 错误被静默吞掉,且 SSE 换行格式需确认

两个关注点:

  1. 错误处理:第 171-173 行在 chunk.Err != nil 时仅 break 并打印日志,函数最终返回 nil(第 200 行)。调用方无法感知流中途出错。同一问题也存在于 cliproxyapi_codex/adapter.go(第 135-137 行),而其他 adapter(如 antigravitycustom)均正确使用 domain.NewProxyErrorWithMessage 返回错误。建议改为返回错误,让上游能够捕获和记录流中断事件。

  2. 换行格式:第 180-181 行在 chunk.Payload 后追加了 \n。由于代码随后调用 usage.ExtractFromStreamContent(sseBuffer.String()) 来解析 SSE 内容,需确认 executor.ExecuteStream 返回的 payload 是否已包含 SSE 格式的终止符(\n\n)。如果已包含,额外的 \n 可能导致事件解析异常。

🤖 Prompt for AI Agents
In `@internal/adapter/provider/cliproxyapi_antigravity/adapter.go` around lines
170 - 184, The stream loop currently swallows chunk errors (if chunk.Err != nil
it only logs and breaks) and always returns nil, so change the handler in the
loop inside ExecuteStream's consumer (the for chunk := range stream block) to
return a domain.NewProxyErrorWithMessage (or equivalent used by other adapters)
when chunk.Err is non-nil so the caller can observe the failure; also stop
unconditionally appending an extra '\n' after writing chunk.Payload to w and to
sseBuffer — instead, inspect/assume the payload format from
executor.ExecuteStream and only add terminators if payloads do not already
include the SSE delimiter (used by usage.ExtractFromStreamContent and
sseBuffer), ensuring w.Write/ flusher.Flush behavior remains unchanged.

Comment on lines +89 to +112
func (a *CLIProxyAPICodexAdapter) executeNonStream(ctx context.Context, w http.ResponseWriter, execReq executor.Request, execOpts executor.Options) error {
resp, err := a.executor.Execute(ctx, a.authObj, execReq, execOpts)
if err != nil {
return domain.NewProxyErrorWithMessage(err, true, fmt.Sprintf("executor request failed: %v", err))
}

// Extract and send token usage metrics
if eventChan := ctxutil.GetEventChan(ctx); eventChan != nil {
if metrics := usage.ExtractFromResponse(string(resp.Payload)); metrics != nil {
// Adjust for Codex: input_tokens includes cached_tokens
metrics = usage.AdjustForClientType(metrics, domain.ClientTypeCodex)
eventChan.SendMetrics(&domain.AdapterMetrics{
InputTokens: metrics.InputTokens,
OutputTokens: metrics.OutputTokens,
})
}
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(resp.Payload)

return nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

非流式路径缺少响应模型提取和响应信息事件。

与原始 Codex adapter(codex/adapter.go 第 257-274 行)相比,此路径缺少:

  1. 响应信息事件eventChan.SendResponseInfo)—— 影响请求调试和追踪。
  2. 响应模型提取extractModelFromResponse / SendResponseModel)—— 影响模型使用统计。
  3. 缓存相关指标CacheReadCount 等)—— AdapterMetrics 只发送了 InputTokensOutputTokens,缺少缓存指标。

这些遗漏会导致通过 CLIProxyAPI 路径的请求在可观测性方面劣于直连路径。

建议补充响应信息事件和模型提取
 func (a *CLIProxyAPICodexAdapter) executeNonStream(ctx context.Context, w http.ResponseWriter, execReq executor.Request, execOpts executor.Options) error {
 	resp, err := a.executor.Execute(ctx, a.authObj, execReq, execOpts)
 	if err != nil {
 		return domain.NewProxyErrorWithMessage(err, true, fmt.Sprintf("executor request failed: %v", err))
 	}
 
+	// Send response info via EventChannel
+	if eventChan := ctxutil.GetEventChan(ctx); eventChan != nil {
+		eventChan.SendResponseInfo(&domain.ResponseInfo{
+			Status: http.StatusOK,
+			Body:   string(resp.Payload),
+		})
+	}
+
 	// Extract and send token usage metrics
 	if eventChan := ctxutil.GetEventChan(ctx); eventChan != nil {
🤖 Prompt for AI Agents
In `@internal/adapter/provider/cliproxyapi_codex/adapter.go` around lines 89 -
112, In executeNonStream, after extracting usage metrics you must also emit the
missing response info and response model events and include cache metrics in
AdapterMetrics: call extractModelFromResponse(resp.Payload) and if non-nil call
eventChan.SendResponseModel with the model, call eventChan.SendResponseInfo
similar to codex/adapter.go, and when sending AdapterMetrics include
cache-related fields from metrics (e.g., CacheReadCount,
CacheMissCount/CacheHitCount, CachedTokens) in addition to InputTokens and
OutputTokens so CLIProxyAPICodexAdapter matches the observability behavior of
the original codex adapter.

- 补全 SendResponseInfo/SendResponseModel/TTFT 事件上报
- 修复 SSE 流重复分隔符、stream error 静默吞没
- updateModelInBody JSON 解析错误标记为不可重试
- 前端 optimistic update 在 config 检查前不再设置状态
- CLIProxyAPI Switch 添加 focus-visible 和 aria-label
- lint: interface{} → any, strings.Split → strings.SplitSeq

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@web/src/pages/providers/components/codex-provider-view.tsx`:
- Around line 536-539: Replace the hard-coded "CLIProxyAPI" label with the i18n
translation call used elsewhere in this file (e.g. use the existing t() from
react-i18next); locate the JSX block in the CodexProviderView component where
the div contains "CLIProxyAPI" and change it to t('providers.cliProxyApi') (or
an appropriate key consistent with other keys in this file), updating any
translation JSON entries accordingly.
🧹 Nitpick comments (6)
internal/adapter/provider/cliproxyapi_codex/adapter.go (2)

106-112: Codex 适配器的 AdapterMetrics 未传递缓存指标字段,与 Antigravity 适配器不一致。

对比 cliproxyapi_antigravity/adapter.go(Lines 139-146)传递了 CacheReadCountCacheCreationCount 等缓存字段,而 Codex 适配器仅传递 InputTokensOutputTokens。虽然 usage.AdjustForClientType 可能将缓存字段归零,但如果后续 Codex 支持缓存指标,此处会丢失数据。

建议统一传递所有可用字段,与 Antigravity 适配器保持一致:

♻️ 建议修改(非流式和流式路径均需修改)
 			eventChan.SendMetrics(&domain.AdapterMetrics{
-				InputTokens:  metrics.InputTokens,
-				OutputTokens: metrics.OutputTokens,
+				InputTokens:          metrics.InputTokens,
+				OutputTokens:         metrics.OutputTokens,
+				CacheReadCount:       metrics.CacheReadCount,
+				CacheCreationCount:   metrics.CacheCreationCount,
+				Cache5mCreationCount: metrics.Cache5mCreationCount,
+				Cache1hCreationCount: metrics.Cache1hCreationCount,
 			})

206-236: extractModelFromResponseextractModelFromSSE 与 Antigravity 适配器完全重复。

这两个辅助函数在 cliproxyapi_antigravity/adapter.go(Lines 244-272)中有完全相同的实现。建议提取到共享的工具包中(如 internal/usage 或新的 internal/adapter/provider/common),减少维护负担。

internal/adapter/provider/cliproxyapi_antigravity/adapter.go (3)

66-66: 生产代码中遗留了 log.Printf 调试日志。

Line 66 在每次请求时都会打印 requestModelmappedModelclientType,在高流量场景下会产生大量日志噪音。建议降级为 debug 级别或移除。


114-121: updateModelInBody 通过 JSON 反序列化/序列化替换模型字段,会改变 payload 格式。

json.Unmarshal → 修改 → json.Marshal 的往返操作会导致 JSON key 顺序变化、空白符消失以及数字精度可能丢失(如 float64 精度问题)。对于大多数 API 场景这不是问题,但如果下游依赖原始 payload 格式,可能会出现意外行为。

如果需要保留原始格式,可以考虑使用 json.RawMessage 或正则替换。当前实现对正常场景可接受。


1-272: Antigravity 和 Codex 两个 CLIProxyAPI 适配器之间存在大量代码重复。

以下函数/方法在两个适配器中几乎完全相同:

  • executeNonStream / executeStream(除缓存指标差异外)
  • extractModelFromResponse / extractModelFromSSE

主要差异仅在 Execute 方法中的客户端类型路由和模型替换逻辑。建议考虑提取公共的流式/非流式执行逻辑到一个共享基础结构或工具包中,减少未来维护和同步成本。

web/src/pages/providers/components/codex-provider-view.tsx (1)

366-379: 仅传递需要更新的字段,而不是展开整个 provider 对象。

当前实现展开了整个 provider 对象后再覆盖 config,这会将 idcreatedAtupdatedAtsupportedClientTypes 等不必要的字段一并发送。虽然 Partial<Provider> 允许这样做,但 provider-edit-flow.tsx 的做法展示了更好的模式——仅构造需要更新的字段,更清晰且与代码库中的其他部分保持一致。

♻️ 建议修改
       await updateProvider.mutateAsync({
         id: provider.id,
         data: {
-          ...provider,
+          type: provider.type,
+          name: provider.name,
           config: {
             ...provider.config,
             codex: {
               ...config,
               useCLIProxyAPI: checked,
             },
           },
         },
       });
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fac48c6 and 31f3d2c.

📒 Files selected for processing (5)
  • internal/adapter/provider/cliproxyapi_antigravity/adapter.go
  • internal/adapter/provider/cliproxyapi_codex/adapter.go
  • web/src/pages/providers/components/antigravity-provider-view.tsx
  • web/src/pages/providers/components/cliproxyapi-switch.tsx
  • web/src/pages/providers/components/codex-provider-view.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/pages/providers/components/antigravity-provider-view.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
internal/adapter/provider/cliproxyapi_antigravity/adapter.go (6)
internal/adapter/provider/antigravity/adapter.go (1)
  • NewAdapter (41-71)
internal/adapter/provider/antigravity/transform_request.go (1)
  • Metadata (155-157)
internal/context/context.go (6)
  • GetClientType (82-87)
  • GetRequestBody (138-143)
  • GetIsStream (181-186)
  • GetRequestModel (110-115)
  • GetMappedModel (117-122)
  • GetEventChan (203-208)
internal/domain/errors.go (1)
  • NewProxyErrorWithMessage (61-63)
internal/usage/extractor.go (2)
  • ExtractFromResponse (31-49)
  • ExtractFromStreamContent (294-296)
internal/domain/adapter_event.go (1)
  • AdapterMetrics (20-27)
web/src/pages/providers/components/codex-provider-view.tsx (3)
web/src/lib/transport/http-transport.ts (1)
  • updateProvider (113-116)
web/src/hooks/queries/index.ts (1)
  • useUpdateProvider (11-11)
web/src/hooks/queries/use-providers.ts (1)
  • useUpdateProvider (50-62)
🔇 Additional comments (2)
web/src/pages/providers/components/cliproxyapi-switch.tsx (1)

1-40: 之前的无障碍性问题已修复,组件实现良好。

aria-labelfocus-visible 样式以及 role="switch" / aria-checked 属性均已正确设置,键盘可访问性和屏幕阅读器支持到位。

internal/adapter/provider/cliproxyapi_codex/adapter.go (1)

220-234: 项目已声明支持 Go 1.25,strings.SplitSeq 的用法兼容。

项目的 go.mod 中已指定 go 1.25,满足 strings.SplitSeq API 在 Go 1.24+ 的要求,无兼容性问题。

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

awsl233777 and others added 2 commits February 6, 2026 12:07
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dreamhunter2333 dreamhunter2333 merged commit a43c7f0 into main Feb 6, 2026
2 checks passed
@dreamhunter2333 dreamhunter2333 deleted the feat/cliproxyapi-integration branch February 6, 2026 04:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants