Skip to content

feat(providers): add Grok CPA flow and cooldown fallback setting - #678

Merged
awsl233777 merged 2 commits into
mainfrom
fix/rate-limit-cooldown-setting
Jul 15, 2026
Merged

feat(providers): add Grok CPA flow and cooldown fallback setting#678
awsl233777 merged 2 commits into
mainfrom
fix/rate-limit-cooldown-setting

Conversation

@awsl233777

@awsl233777 awsl233777 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add Grok CPA provider management flow and provider UI support
  • make the local cooldown fallback for rate/concurrent-limit errors configurable
  • keep upstream-provided cooldown signals authoritative: quota reset/CooldownUntil and Retry-After/RetryInfo are not overridden by the setting

Cooldown fallback scope

  • controls only ReasonRateLimit and ReasonConcurrentLimit when no upstream retry/cooldown time is provided
  • does not control quota reset, quota fallback, Retry-After, or RetryInfo
  • keeps internal/cooldown/policy.go default 5s policy intact and applies the setting in executor handling only for the targeted fallback path

Validation

  • go test ./internal/adapter/provider/antigravity ./internal/executor ./internal/cooldown ./internal/handler ./internal/service

Summary by CodeRabbit

  • 新功能

    • 新增 Grok(xAI)提供商支持,兼容 OpenAI 客户端。
    • 支持通过 OAuth JSON 单个或批量导入凭据,并配置模型映射。
    • 新增 Grok 提供商创建、编辑、删除及导出设置页面。
    • 敏感令牌在编辑和更新时安全保留与脱敏。
  • 问题修复

    • 优化速率限制与并发限制的冷却时间处理,优先使用上游重试时间,并支持配置默认冷却时长。

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

本次变更新增 Grok/XAI OAuth provider 的后端适配器与前端创建、编辑、导出流程,并完善密钥保留与脱敏处理。同时调整 Antigravity 限流解析及默认冷却时间配置。

Changes

Grok 后端适配与限流处理

Layer / File(s) Summary
限流解析与默认冷却
internal/adapter/provider/antigravity/*, internal/executor/*, internal/domain/model.go, internal/service/system_setting_validation.go
区分请求级限流与配额耗尽响应,为速率或并发限制增加可配置默认冷却时间,并新增相关测试。
Grok 后端适配器与注册
internal/adapter/provider/cliproxyapi_grok/*, internal/adapter/provider/grok/*, internal/core/database.go, cmd/maxx/main.go, go.mod
新增 XAI OAuth 配置解析、OpenAI 兼容请求执行、非流式与 SSE 流式响应处理,并注册 Grok provider。
配置保全与接口验证
internal/service/admin.go, internal/handler/self_service.go, tests/e2e/grok_provider_test.go
支持 Grok 密钥继承、敏感字段清理、OpenAI 客户端能力设置,并验证创建、更新和导出行为。

Grok 前端流程

Layer / File(s) Summary
类型与创建流程
web/src/lib/*, web/src/index.css, web/src/locales/*, web/src/pages/providers/types.ts, web/src/pages/providers/components/select-type-step.tsx, web/src/pages/providers/components/grok-token-import.tsx, web/src/pages/providers/create-layout.tsx, web/src/pages/providers/hooks/*
新增 Grok 类型、颜色、文案、JSON 凭证导入、模型映射编辑、provider 类型选择和创建路由。
编辑页面
web/src/pages/providers/components/grok-provider-view.tsx, web/src/pages/providers/components/provider-edit-flow.tsx
新增 Grok provider 编辑视图,支持基本信息、错误冷却、导出可见性、模型映射和删除操作。

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GrokTokenImport
  participant ProviderAPI
  participant CLIProxyAPIGrokAdapter
  participant CLIProxyAPI
  Client->>GrokTokenImport: submit XAI OAuth JSON
  GrokTokenImport->>ProviderAPI: create Grok provider
  ProviderAPI->>CLIProxyAPIGrokAdapter: initialize adapter
  CLIProxyAPIGrokAdapter->>CLIProxyAPI: execute OpenAI-compatible request
  CLIProxyAPI-->>CLIProxyAPIGrokAdapter: return JSON or SSE
  CLIProxyAPIGrokAdapter-->>Client: write response
Loading

Possibly related PRs

Suggested reviewers: bowl42, ymkiux

Poem

我是小兔,蹦进 Grok 的紫色田,
OAuth 凭证排队来,模型映射连成线。
流式响应像胡萝卜,咔嚓传到客户端,
限流冷却有了钟,
新 provider 安稳落家园。

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了这次提交的两项主要变化:Grok CPA 流程和可配置的冷却回退设置。
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/rate-limit-cooldown-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.

❤️ Share

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

@awsl233777 awsl233777 changed the title feat(grok): add CPA provider management flow feat(providers): add Grok CPA flow and cooldown fallback setting Jul 15, 2026
@awsl233777
awsl233777 merged commit 5c4f0aa into main Jul 15, 2026
5 of 6 checks passed
@awsl233777
awsl233777 deleted the fix/rate-limit-cooldown-setting branch July 15, 2026 06:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
internal/domain/model.go (1)

309-310: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

明确 OAuth 密钥的导出策略。

这里要求密钥在所有 export 场景中脱敏,但管理导出测试明确要求保留令牌以支持重新导入。请改为仅禁止自助/UI 响应泄漏,并注明受控管理导出是例外,避免后续按错误安全契约修改实现。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/domain/model.go` around lines 309 - 310, 更新 ProviderConfigGrok
的安全注释,明确令牌仅在自助服务和 UI 响应中必须脱敏;注明受控管理导出是例外,可保留令牌以支持重新导入,避免将脱敏要求扩大到所有导出场景。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/adapter/provider/cliproxyapi_grok/adapter.go`:
- Around line 170-176: Update the error mapping used by executeNonStream and its
streaming counterpart to preserve executor status-code semantics: when the
wrapped error contains a status code, map Scope, Reason, Retryable, and
HTTPStatusCode consistently for authentication, quota, and rate-limit responses
instead of always assigning provider/server-error values. Centralize this
mapping in one shared helper and reuse it from both flows.

In `@web/src/pages/providers/components/grok-provider-view.tsx`:
- Around line 48-67: Update the grok submission payload so the trimmed email and
baseURL values use undefined rather than grok?.email or grok?.baseURL when
empty, allowing users to clear these fields while preserving non-empty input
values.

In `@web/src/pages/providers/components/grok-token-import.tsx`:
- Line 289: Update the back navigation in the relevant provider token import
component to wrap the ChevronLeft icon in a focusable Button with an appropriate
aria-label, and move the click handler onto the Button while preserving
navigation to /providers/create.
- Around line 238-266: Update the submit flow around createProvider.mutateAsync
and createModelMapping.mutateAsync so the entire import is atomic: prefer a
backend batch operation that creates all providers and mappings together, or
reliably delete every resource created during this submission when any request
fails. Ensure retries cannot leave partial imports or duplicate providers, while
preserving the existing item and mapping data.
- Around line 150-159: 将 normalizeGrokConfig
中直接面向用户的英文错误改为可本地化的错误码或结构化错误信息,并在导入组件使用 t(...) 将其转换为当前语言后展示;覆盖 type、auth_kind 及
access_token/refresh_token 校验路径,同时保持原有校验条件和失败行为不变。

In `@web/src/pages/providers/components/select-type-step.tsx`:
- Around line 308-313: Replace the hardcoded “Grok” title and “Import xAI OAuth
JSON from CLIProxyAPI” description in the provider card with the file’s existing
t() i18n lookup pattern. Add or reuse the corresponding translation keys while
preserving the card’s current layout and styling.

---

Nitpick comments:
In `@internal/domain/model.go`:
- Around line 309-310: 更新 ProviderConfigGrok 的安全注释,明确令牌仅在自助服务和 UI
响应中必须脱敏;注明受控管理导出是例外,可保留令牌以支持重新导入,避免将脱敏要求扩大到所有导出场景。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 08796aa0-d1e2-44d4-83dd-c10cd3185463

📥 Commits

Reviewing files that changed from the base of the PR and between ae0ada8 and 7833603.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (27)
  • cmd/maxx/main.go
  • go.mod
  • internal/adapter/provider/antigravity/adapter.go
  • internal/adapter/provider/antigravity/rate_limit_info_test.go
  • internal/adapter/provider/cliproxyapi_grok/adapter.go
  • internal/adapter/provider/cliproxyapi_grok/adapter_test.go
  • internal/adapter/provider/grok/adapter.go
  • internal/core/database.go
  • internal/domain/model.go
  • internal/executor/cooldown_default_test.go
  • internal/executor/executor.go
  • internal/handler/self_service.go
  • internal/service/admin.go
  • internal/service/system_setting_validation.go
  • tests/e2e/grok_provider_test.go
  • web/src/index.css
  • web/src/lib/theme.ts
  • web/src/lib/transport/types.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/providers/components/grok-provider-view.tsx
  • web/src/pages/providers/components/grok-token-import.tsx
  • web/src/pages/providers/components/provider-edit-flow.tsx
  • web/src/pages/providers/components/select-type-step.tsx
  • web/src/pages/providers/create-layout.tsx
  • web/src/pages/providers/hooks/use-provider-navigation.ts
  • web/src/pages/providers/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: multiinstance
  • GitHub Check: playwright
  • GitHub Check: e2e
🔇 Additional comments (27)
internal/adapter/provider/antigravity/adapter.go (1)

1190-1198: LGTM!

Also applies to: 1236-1236, 1261-1261

internal/executor/executor.go (1)

262-273: LGTM!

Also applies to: 297-314

internal/service/system_setting_validation.go (1)

5-5: LGTM!

Also applies to: 19-37

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

1-151: LGTM!

internal/executor/cooldown_default_test.go (1)

1-109: LGTM!

web/src/lib/transport/types.ts (1)

117-137: LGTM!

Also applies to: 171-171

web/src/index.css (1)

100-100: LGTM!

Also applies to: 387-387

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

1881-1900: LGTM!

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

1877-1896: LGTM!

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

1-149: LGTM!

Also applies to: 160-237, 277-288, 290-405

web/src/pages/providers/create-layout.tsx (1)

7-7: LGTM!

Also applies to: 24-24

web/src/lib/theme.ts (1)

24-25: LGTM!

web/src/pages/providers/types.ts (1)

86-93: LGTM!

web/src/pages/providers/hooks/use-provider-navigation.ts (1)

15-15: LGTM!

web/src/pages/providers/components/provider-edit-flow.tsx (1)

671-690: LGTM!

internal/domain/model.go (1)

311-329: LGTM!

Also applies to: 341-341, 852-852

internal/adapter/provider/cliproxyapi_grok/adapter.go (2)

1-163: LGTM!

Also applies to: 165-169, 178-190, 209-241, 248-277


193-197: 🎯 Functional Correctness

这里不需要强制报错。 Flusher 不可用时回退到 executeNonStreaminternal/adapter/provider/cliproxyapi_codex/adapter.go 的处理一致;此路径会走 Execute 并按普通 JSON 返回,不会继续按 SSE 流式写出。

			> Likely an incorrect or invalid review comment.
internal/adapter/provider/cliproxyapi_grok/adapter_test.go (1)

1-56: LGTM!

internal/service/admin.go (1)

516-532: LGTM!

Also applies to: 1340-1342

tests/e2e/grok_provider_test.go (1)

1-145: LGTM!

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

9-14: LGTM!

internal/core/database.go (1)

17-17: LGTM!

cmd/maxx/main.go (1)

22-22: LGTM!

go.mod (2)

53-53: LGTM!


126-126: 🎯 Functional Correctness

请验证新的 CLIProxyAPI 版本与适配器契约兼容。

replace 实际改变了运行时依赖;请确认目标提交可解析,并验证 cliproxyapi_grok 使用的 API、模型映射及流式响应行为仍兼容,随后运行对应的定向测试。

internal/handler/self_service.go (1)

1471-1477: LGTM!

Comment on lines +170 to +176
resp, err := a.executor.Execute(ctx, a.authObj, execReq, execOpts)
if err != nil {
log.Printf("[CLIProxyAPI-Grok] executeNonStream error: model=%s, err=%v", execReq.Model, err)
proxyErr := domain.NewProxyErrorWithMessage(err, true, fmt.Sprintf("executor request failed: %v", err))
proxyErr.Scope = domain.ScopeProvider
proxyErr.Reason = domain.CooldownReasonServerError
return proxyErr

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 查找项目已有的状态码和冷却原因映射实现。
rg -n -C5 --type go 'StatusCode\(\)|CooldownReason|errors\.As\(' internal

Repository: awsl-project/maxx

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== adapter outline =="
ast-grep outline internal/adapter/provider/cliproxyapi_grok/adapter.go --view expanded || true

echo
echo "== adapter snippets around the relevant lines =="
sed -n '140,230p' internal/adapter/provider/cliproxyapi_grok/adapter.go | cat -n

echo
echo "== executor error type references =="
sed -n '1,260p' internal/executor/types.go 2>/dev/null | cat -n || true

echo
echo "== search for executor.StatusError and status mapping =="
rg -n -C4 'type .*StatusError|StatusError|HTTPStatus|StatusCode|status code|CooldownReasonAuthFailure|CooldownReasonRateLimitExceeded|CooldownReasonQuotaExhausted' internal/executor internal/adapter/provider/cliproxyapi_grok internal/domain

Repository: awsl-project/maxx

Length of output: 25546


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== executor interface / error types =="
rg -n -C4 'type .*Executor|type .*StatusError|HTTPStatusCode|StatusError|Upstream.*Error|CooldownReasonAuthFailure|CooldownReasonRateLimitExceeded|CooldownReasonQuotaExhausted' internal/executor internal/adapter/provider/cliproxyapi_grok | sed -n '1,260p'

echo
echo "== cliproxyapi_grok stream path snippet =="
sed -n '193,250p' internal/adapter/provider/cliproxyapi_grok/adapter.go | cat -n

echo
echo "== domain ProxyError fields and helpers =="
sed -n '55,130p' internal/domain/errors.go | cat -n

Repository: awsl-project/maxx

Length of output: 12029


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== cliproxyapi_grok imports and surrounding logic =="
sed -n '1,120p' internal/adapter/provider/cliproxyapi_grok/adapter.go | cat -n

echo
echo "== executor package API surface =="
sed -n '1,220p' internal/executor/types.go 2>/dev/null | cat -n || true
sed -n '1,220p' internal/executor/executor.go | cat -n | sed -n '1,220p'

echo
echo "== search for 401/402/429 handling and StatusError across repo =="
rg -n -C3 '401|402|429|StatusError|HTTPStatusCode|errors\.As\(' internal | sed -n '1,260p'

Repository: awsl-project/maxx

Length of output: 30074


🌐 Web query:

router-for-me/CLIProxyAPI sdk/cliproxy/executor/types.go StatusError HTTPStatusCode 401 402 429

💡 Result:

In the router-for-me/CLIProxyAPI project, StatusError is an interface defined within the sdk/cliproxy/executor package [1]. It requires types to implement an error method and a StatusCode method that returns an integer [1]. While StatusError itself is a generic interface, the CLIProxyAPI system uses it to handle specific HTTP status codes during execution and authentication workflows [2][3][4][5]. The status codes 401, 402, and 429 are frequently handled in the following ways: - 401 Unauthorized: Triggers an update to authentication state, often marking it as unauthorized and initiating a cooling-off period (e.g., 30 minutes) before the model can be retried [2][3]. - 402 Payment Required: Treated similarly to 401, this indicates billing issues and typically sets a "payment_required" status and a retry delay [2][3]. - 429 Too Many Requests: Indicates that quota has been exhausted; the system sets the status to "quota exhausted," marks the quota as exceeded, and calculates a recovery time [2][3]. These codes are checked by casting errors to the StatusError interface (or an anonymous interface with a StatusCode method) to decide how to manage backoff, model suspension, and quota tracking [2][3][4][5].

Citations:


保留 executor 的状态码语义。
这里把所有错误都归为 ScopeProvider + CooldownReasonServerError,会丢掉 401/402/429 的鉴权、配额和限流语义。若底层错误链里带状态码,就在同一个 helper 里统一映射 Scope / Reason / Retryable / HTTPStatusCode,让流式和非流式共用。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/adapter/provider/cliproxyapi_grok/adapter.go` around lines 170 -
176, Update the error mapping used by executeNonStream and its streaming
counterpart to preserve executor status-code semantics: when the wrapped error
contains a status code, map Scope, Reason, Retryable, and HTTPStatusCode
consistently for authentication, quota, and rate-limit responses instead of
always assigning provider/server-error values. Centralize this mapping in one
shared helper and reuse it from both flows.

Comment on lines +48 to +67
grok: {
type: 'xai',
authKind: 'oauth',
email: email.trim() || grok?.email,
sub: grok?.sub,
// Blank/omitted secrets preserve the stored tokens on the backend.
accessToken: '',
refreshToken: '',
idToken: '',
tokenType: grok?.tokenType,
expiresIn: grok?.expiresIn,
expired: grok?.expired,
lastRefresh: grok?.lastRefresh,
redirectURI: grok?.redirectURI,
tokenEndpoint: grok?.tokenEndpoint,
baseURL: baseURL.trim() || grok?.baseURL,
disabled: grok?.disabled,
headers: grok?.headers,
},
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

修复回退逻辑,允许用户清空字段。

因为状态 emailbaseURL 已使用旧值进行了初始化,在提交表单时,如果用户故意将这些输入框清空,email.trim() 将返回 "",紧随其后的 || grok?.email 会导致原有的非空旧值被重新赋上。这会导致用户无法成功清空这些配置项。

建议将回退逻辑修改为 || undefined,以便正确处理清空操作并向后端发送 undefined 从而抹去相应配置。

🐛 建议的修复
           grok: {
             type: 'xai',
             authKind: 'oauth',
-            email: email.trim() || grok?.email,
+            email: email.trim() || undefined,
             sub: grok?.sub,
             // Blank/omitted secrets preserve the stored tokens on the backend.
             accessToken: '',
             refreshToken: '',
             idToken: '',
             tokenType: grok?.tokenType,
             expiresIn: grok?.expiresIn,
             expired: grok?.expired,
             lastRefresh: grok?.lastRefresh,
             redirectURI: grok?.redirectURI,
             tokenEndpoint: grok?.tokenEndpoint,
-            baseURL: baseURL.trim() || grok?.baseURL,
+            baseURL: baseURL.trim() || undefined,
             disabled: grok?.disabled,
             headers: grok?.headers,
           },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
grok: {
type: 'xai',
authKind: 'oauth',
email: email.trim() || grok?.email,
sub: grok?.sub,
// Blank/omitted secrets preserve the stored tokens on the backend.
accessToken: '',
refreshToken: '',
idToken: '',
tokenType: grok?.tokenType,
expiresIn: grok?.expiresIn,
expired: grok?.expired,
lastRefresh: grok?.lastRefresh,
redirectURI: grok?.redirectURI,
tokenEndpoint: grok?.tokenEndpoint,
baseURL: baseURL.trim() || grok?.baseURL,
disabled: grok?.disabled,
headers: grok?.headers,
},
},
grok: {
type: 'xai',
authKind: 'oauth',
email: email.trim() || undefined,
sub: grok?.sub,
// Blank/omitted secrets preserve the stored tokens on the backend.
accessToken: '',
refreshToken: '',
idToken: '',
tokenType: grok?.tokenType,
expiresIn: grok?.expiresIn,
expired: grok?.expired,
lastRefresh: grok?.lastRefresh,
redirectURI: grok?.redirectURI,
tokenEndpoint: grok?.tokenEndpoint,
baseURL: baseURL.trim() || undefined,
disabled: grok?.disabled,
headers: grok?.headers,
},
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/providers/components/grok-provider-view.tsx` around lines 48 -
67, Update the grok submission payload so the trimmed email and baseURL values
use undefined rather than grok?.email or grok?.baseURL when empty, allowing
users to clear these fields while preserving non-empty input values.

Comment on lines +150 to +159
function normalizeGrokConfig(raw: CPAxAIExportJSON): ProviderConfigGrok {
if (raw.type !== 'xai') {
throw new Error(`Expected CPA xai credential JSON, got type=${raw.type || '(empty)'}`);
}
if ((raw.auth_kind || 'oauth') !== 'oauth') {
throw new Error(`Expected oauth credential JSON, got auth_kind=${raw.auth_kind || '(empty)'}`);
}
if (!raw.access_token && !raw.refresh_token) {
throw new Error('access_token or refresh_token is required');
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

将导入错误信息纳入本地化。

这些错误会直接展示给用户,但目前固定为英文,中文界面会出现混合语言。请抛出错误码并在组件中通过 t(...) 转换,或向校验函数传入本地化文案。

Also applies to: 243-244

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/providers/components/grok-token-import.tsx` around lines 150 -
159, 将 normalizeGrokConfig 中直接面向用户的英文错误改为可本地化的错误码或结构化错误信息,并在导入组件使用 t(...)
将其转换为当前语言后展示;覆盖 type、auth_kind 及 access_token/refresh_token
校验路径,同时保持原有校验条件和失败行为不变。

Comment on lines +238 to +266
const submit = async () => {
setError(null);
setSaveStatus('idle');
setSubmitting(true);
try {
const items = fileItems.length > 0 ? fileItems : parseImportItemsFromText(jsonText);
if (items.length === 0) throw new Error('Paste JSON or select files');

const mappingEntries = Object.entries(modelMapping);
for (const item of items) {
const grok = normalizeGrokConfig(item.raw);
const data: CreateProviderData = {
type: 'grok',
name: providerName(grok, item.source),
config: { disableErrorCooldown, grok },
supportedClientTypes: [...GROK_CLIENT_TYPES],
excludeFromExport: blackBox,
blackBox,
};
const provider = await createProvider.mutateAsync(data);
for (const [pattern, target] of mappingEntries) {
await createModelMapping.mutateAsync({
scope: 'provider',
providerID: provider.id,
pattern,
target,
});
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

避免失败后留下部分导入结果。

Provider 创建和模型映射是多个独立写请求。任意后续请求失败时,已创建的 Provider/映射仍会保留;用户重试还可能生成重复 Provider。请使用后端原子批量接口,或在失败时可靠回滚本次已创建的资源。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/providers/components/grok-token-import.tsx` around lines 238 -
266, Update the submit flow around createProvider.mutateAsync and
createModelMapping.mutateAsync so the entire import is atomic: prefer a backend
batch operation that creates all providers and mappings together, or reliably
delete every resource created during this submission when any request fails.
Ensure retries cannot leave partial imports or duplicate providers, while
preserving the existing item and mapping data.

return (
<div className="flex h-full flex-col">
<PageHeader
icon={<ChevronLeft className="cursor-pointer" onClick={() => navigate('/providers/create')} />}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

将返回操作改为可聚焦的按钮。

点击事件直接绑定在 SVG 上,键盘用户无法聚焦或触发该操作。请使用带 aria-labelButton 包裹图标。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/providers/components/grok-token-import.tsx` at line 289, Update
the back navigation in the relevant provider token import component to wrap the
ChevronLeft icon in a focusable Button with an appropriate aria-label, and move
the click handler onto the Button while preserving navigation to
/providers/create.

Comment on lines +308 to +313
<div className="flex-1 min-w-0 space-y-1">
<h3 className="text-sm sm:text-base font-semibold text-foreground leading-tight truncate">Grok</h3>
<p className="text-xs sm:text-sm text-muted-foreground leading-relaxed line-clamp-2">
Import xAI OAuth JSON from CLIProxyAPI
</p>
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

使用 i18n 函数替换硬编码的文本。

当前卡片的标题和描述被硬编码为英文,这会破坏界面的国际化支持。建议统一使用 t() 函数来获取多语言文本(与文件中其他 Provider 卡片保持一致)。

♻️ 建议的代码修改
                     <div className="flex-1 min-w-0 space-y-1">
-                      <h3 className="text-sm sm:text-base font-semibold text-foreground leading-tight truncate">Grok</h3>
-                      <p className="text-xs sm:text-sm text-muted-foreground leading-relaxed line-clamp-2">
-                        Import xAI OAuth JSON from CLIProxyAPI
-                      </p>
+                      <h3 className="text-sm sm:text-base font-semibold text-foreground leading-tight truncate">
+                        {t('addProvider.grok.name', 'Grok')}
+                      </h3>
+                      <p className="text-xs sm:text-sm text-muted-foreground leading-relaxed line-clamp-2">
+                        {t('addProvider.grok.description', 'Import xAI OAuth JSON from CLIProxyAPI')}
+                      </p>
                     </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="flex-1 min-w-0 space-y-1">
<h3 className="text-sm sm:text-base font-semibold text-foreground leading-tight truncate">Grok</h3>
<p className="text-xs sm:text-sm text-muted-foreground leading-relaxed line-clamp-2">
Import xAI OAuth JSON from CLIProxyAPI
</p>
</div>
<div className="flex-1 min-w-0 space-y-1">
<h3 className="text-sm sm:text-base font-semibold text-foreground leading-tight truncate">
{t('addProvider.grok.name', 'Grok')}
</h3>
<p className="text-xs sm:text-sm text-muted-foreground leading-relaxed line-clamp-2">
{t('addProvider.grok.description', 'Import xAI OAuth JSON from CLIProxyAPI')}
</p>
</div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/providers/components/select-type-step.tsx` around lines 308 -
313, Replace the hardcoded “Grok” title and “Import xAI OAuth JSON from
CLIProxyAPI” description in the provider card with the file’s existing t() i18n
lookup pattern. Add or reuse the corresponding translation keys while preserving
the card’s current layout and styling.

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.

1 participant