feat: 集成 CLIProxyAPI 作为 Antigravity/Codex 的可选转发后端#186
Conversation
在原生 Antigravity 和 Codex provider 配置中新增 useCLIProxyAPI 开关, 启用后 NewAdapter() 直接委托给 CLIProxyAPI adapter 处理请求,无需独立 的 provider 类型。前端在创建页和编辑页均可控制此开关,编辑页支持 即时切换并自动保存(含失败回滚)。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthrough为 Antigravity 与 Codex 提供商引入可选的 CLIProxyAPI 转发:新增两个 CLIProxyAPI 适配器、后端域模型与配置扩展、前端开关与 UI,且更新 go.mod 添加/替换相关依赖与 replace 指令。 Changes
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: 呈现结果
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
🧹 Recent nitpick comments
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
🧰 Additional context used🧬 Code graph analysis (2)web/src/pages/providers/components/antigravity-provider-view.tsx (3)
web/src/pages/providers/components/codex-provider-view.tsx (3)
🔇 Additional comments (5)
✏️ Tip: You can disable this entire section by setting 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. Comment |
There was a problem hiding this comment.
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 可能产生误导。
ProviderConfigCLIProxyAPIAntigravity和ProviderConfigCLIProxyAPICodex的字段带有 json tag,但它们通过ProviderConfig中的json:"-"标记永远不会被序列化。如果这些 json tag 仅用于文档目的而非实际序列化,建议添加注释说明;如果确实有其他序列化场景(如日志或内部调试),则无需更改。web/src/pages/providers/components/antigravity-token-import.tsx (1)
262-263: 考虑在异步操作期间禁用开关。
CLIProxyAPISwitch在creating或validating状态下未被禁用,用户可以在提交过程中切换状态。虽然由于 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.53与replace指向的伪版本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
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (12)
go.modinternal/adapter/provider/antigravity/adapter.gointernal/adapter/provider/cliproxyapi_antigravity/adapter.gointernal/adapter/provider/cliproxyapi_codex/adapter.gointernal/adapter/provider/codex/adapter.gointernal/domain/model.goweb/src/lib/transport/types.tsweb/src/pages/providers/components/antigravity-provider-view.tsxweb/src/pages/providers/components/antigravity-token-import.tsxweb/src/pages/providers/components/cliproxyapi-switch.tsxweb/src/pages/providers/components/codex-provider-view.tsxweb/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 中,与后端ProviderConfigAntigravity的UseCLIProxyAPI字段对齐。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.
| 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() | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check other adapters' executeStream implementation to confirm SSE chunk format
rg -n --type=go 'flusher\.Flush\(\)' -B5 -A2Repository: 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 -100Repository: 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 -50Repository: 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 1Repository: awsl-project/maxx
Length of output: 831
流式传输中 chunk 错误被静默吞掉,且 SSE 换行格式需确认
两个关注点:
-
错误处理:第 171-173 行在
chunk.Err != nil时仅break并打印日志,函数最终返回nil(第 200 行)。调用方无法感知流中途出错。同一问题也存在于cliproxyapi_codex/adapter.go(第 135-137 行),而其他 adapter(如antigravity、custom)均正确使用domain.NewProxyErrorWithMessage返回错误。建议改为返回错误,让上游能够捕获和记录流中断事件。 -
换行格式:第 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.
| 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 | ||
| } |
There was a problem hiding this comment.
非流式路径缺少响应模型提取和响应信息事件。
与原始 Codex adapter(codex/adapter.go 第 257-274 行)相比,此路径缺少:
- 响应信息事件(
eventChan.SendResponseInfo)—— 影响请求调试和追踪。 - 响应模型提取(
extractModelFromResponse/SendResponseModel)—— 影响模型使用统计。 - 缓存相关指标(
CacheReadCount等)——AdapterMetrics只发送了InputTokens和OutputTokens,缺少缓存指标。
这些遗漏会导致通过 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>
There was a problem hiding this comment.
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)传递了CacheReadCount、CacheCreationCount等缓存字段,而 Codex 适配器仅传递InputTokens和OutputTokens。虽然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:extractModelFromResponse和extractModelFromSSE与 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 在每次请求时都会打印
requestModel、mappedModel和clientType,在高流量场景下会产生大量日志噪音。建议降级为 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,这会将id、createdAt、updatedAt、supportedClientTypes等不必要的字段一并发送。虽然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
📒 Files selected for processing (5)
internal/adapter/provider/cliproxyapi_antigravity/adapter.gointernal/adapter/provider/cliproxyapi_codex/adapter.goweb/src/pages/providers/components/antigravity-provider-view.tsxweb/src/pages/providers/components/cliproxyapi-switch.tsxweb/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-label、focus-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.SplitSeqAPI 在 Go 1.24+ 的要求,无兼容性问题。
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
在原生 Antigravity 和 Codex provider 配置中新增 useCLIProxyAPI 开关, 启用后 NewAdapter() 直接委托给 CLIProxyAPI adapter 处理请求,无需独立 的 provider 类型。前端在创建页和编辑页均可控制此开关,编辑页支持
即时切换并自动保存(含失败回滚)。
Summary by CodeRabbit
新功能
本地化
依赖更新