Codex/dreambrand model aliases - #6529
Conversation
WalkthroughThe pull request adds DreamBrand and ZLHub relay support, Alipay payments, configurable container sources, localized user guides, channel and OAuth updates, and a redesigned default frontend with expanded translations and theme tokens. ChangesPlatform integrations
Frontend and documentation
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1⚔️ Resolve merge conflicts 💡
🧪 Generate unit tests (beta)
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: 11
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
web/default/src/docs/user-guide/en/personal-setting.mdx (1)
54-66: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winKeep localized guide coverage consistent.
The Chinese Personal Settings guide includes sections for available models, notification settings, price settings, IP logs, and API-key reset, while the English and Japanese docs only cover basic info, 2FA, passkeys, and third-party binding. Add equivalent English/Japanese coverage or explicitly document this locale as intentionally incomplete.
🤖 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/default/src/docs/user-guide/en/personal-setting.mdx` around lines 54 - 66, Update the English and Japanese Personal Settings guides to cover the same available models, notification settings, price settings, IP logs, and API-key reset sections documented in the Chinese guide, using equivalent localized headings and instructions; alternatively, explicitly mark the locale documentation as intentionally incomplete if those sections are not supported.web/default/src/features/wallet/lib/ui.tsx (1)
88-96: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winHide the decorative Alipay icon from screen readers.
The icon is rendered alongside the payment method’s visible text, so add
aria-hidden='true'to the sharedSiAlipayelement to avoid redundant announcements.As per coding guidelines, decorative icons must use
aria-hidden="true".🤖 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/default/src/features/wallet/lib/ui.tsx` around lines 88 - 96, Add aria-hidden="true" to the shared SiAlipay element in the PAYMENT_TYPES.ALIPAY and PAYMENT_TYPES.ALIPAY_OFFICIAL cases so the decorative icon is hidden from screen readers.Source: Coding guidelines
web/default/src/features/wallet/hooks/use-payment.ts (1)
90-103: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winReplace the nested ternary with explicit branching.
processPaymentnow uses a second-level ternary for Stripe, Alipay, and the fallback path. Replace it with anif/elsechain or early returns to keep payment routing direct and extensible.As per coding guidelines, two or more nested ternaries are prohibited and complex logic should use clear branches.
🤖 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/default/src/features/wallet/hooks/use-payment.ts` around lines 90 - 103, Replace the nested ternary in processPayment with explicit if/else branching that preserves the existing Stripe, Alipay official, and fallback request routing and payloads. Keep the response assignment or return flow unchanged while making each payment path a clear, directly extensible branch.Source: Coding guidelines
🟡 Minor comments (24)
relay/channel/task/doubao/adaptor_test.go-16-188 (1)
16-188: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse Testify assertions in this new backend test suite.
Replace
t.Fatalfchecks withrequire.NoErrorandrequire.Equal(andassertwhere execution should continue). As per coding guidelines, “New or substantially rewritten Go backend tests must usetestify/requirefor setup and fatal assertions andtestify/assertfor non-fatal checks.”🤖 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 `@relay/channel/task/doubao/adaptor_test.go` around lines 16 - 188, Update the new tests in TestBuildRequestURL, TestZLHubFetchTask, TestZLHubAdaptorMetadata, TestSeedanceMiniVideoInputRatio, TestParseResponsePayloadWithEnvelope, TestParseTaskResultWithEnvelope, and TestConvertToOpenAIVideoWithEnvelope to use testify assertions: replace fatal setup and prerequisite checks with require.NoError or require.Equal, and use assert.Equal or assert.Contains for independent checks that should not stop execution. Add the required testify imports and preserve each test’s existing expectations.Source: Coding guidelines
web/classic/src/components/layout/Footer.jsx-66-82 (1)
66-82: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAlign footer labels with their destination pages.
Mappings such as
联系我们→personal-setting,安装指南→token, and功能特性→pricingappear unrelated to the displayed labels. Map each label to the intended guide page or update the labels.Also applies to: 98-114
🤖 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/classic/src/components/layout/Footer.jsx` around lines 66 - 82, Update the footer anchor mappings in the JSX around the visible `t('关于项目')`, `t('联系我们')`, and the additionally affected links near the pricing entry so every displayed label matches its destination guide page. Correct mismatches such as `联系我们` pointing to `personal-setting`, and review the corresponding `安装指南`/`功能特性` links to either use their intended guide URLs or update labels to reflect those pages.web/classic/src/helpers/render.jsx-406-409 (1)
406-409: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd an icon case for ZLHubAsset.
Line 406 adds adjacent ZLHub mappings, but channel type
59falls through todefaultand renders no icon. Includecase 59with the intended provider icon.🤖 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/classic/src/helpers/render.jsx` around lines 406 - 409, Add a channel-type 59 branch in the icon switch near the existing ZLHub cases in the render helper, returning the intended provider icon for ZLHubAsset. Preserve the current mappings for cases 58 and 60 and ensure type 59 no longer reaches the default branch.relay/channel/dreambrand/adaptor_test.go-139-145 (1)
139-145: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
t.Fatalffrom the httptest handler goroutine.The handler runs on the server's goroutine;
t.Fatalfthere callsruntime.Goexiton the wrong goroutine, so the test isn't reliably failed and the assertion message can be lost. Record witht.Errorfand return instead.💚 Proposed fix
if r.URL.Path != "/ai/v1/images/generations/TASK_1" { - t.Fatalf("query path = %q", r.URL.Path) + t.Errorf("query path = %q", r.URL.Path) + w.WriteHeader(http.StatusInternalServerError) + return } if r.Header.Get("Authorization") != "Bearer upstream-key" { - t.Fatalf("authorization = %q", r.Header.Get("Authorization")) + t.Errorf("authorization = %q", r.Header.Get("Authorization")) + w.WriteHeader(http.StatusUnauthorized) + return }🤖 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 `@relay/channel/dreambrand/adaptor_test.go` around lines 139 - 145, Replace the t.Fatalf assertions inside the httptest server handler with t.Errorf followed by an immediate return, including for the path and Authorization checks. Keep the existing validation messages and ensure the handler does not continue processing after either failed assertion.relay/channel/task/dreambrand/adaptor.go-518-544 (1)
518-544: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
errorReasonleaks raw JSON whenerror.codeis numeric.The inner struct declares
Code string, so an upstream payload like{"error":{"code":30001,"message":"..."}}fails to unmarshal and the function returnsstring(response.Error)— the whole raw object — as the user-facing reason. The image adaptor already types this field asjson.RawMessage.🐛 Proposed fix
var object struct { - Message string `json:"message"` - Msg string `json:"msg"` - Code string `json:"code"` + Message string `json:"message"` + Msg string `json:"msg"` + Code json.RawMessage `json:"code"` } if err := common.Unmarshal(response.Error, &object); err == nil { - for _, value := range []string{object.Message, object.Msg, object.Code} { + for _, value := range []string{object.Message, object.Msg, strings.Trim(string(object.Code), `"`)} { if strings.TrimSpace(value) != "" { return value } } }🤖 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 `@relay/channel/task/dreambrand/adaptor.go` around lines 518 - 544, Update errorReason’s fallback object parsing to represent object.Code as json.RawMessage, matching the image adaptor, so numeric error codes deserialize successfully. Preserve the existing priority of Message, Msg, and Code, and return the decoded code value rather than the raw response.Error object when no message is available.web/default/src/lib/docs-url.ts-1-24 (1)
1-24: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPreserve the active locale in the default docs URL. The resolver hard-codes
/zh/, and its caller supplies no locale, so localized guides cannot open in the visitor’s language.
web/default/src/lib/docs-url.ts#L1-L24: accept a supported locale and build the user-guide path from it, with an explicit fallback.web/default/src/hooks/use-top-nav-links.ts#L59-L91: pass the resolved interface locale toresolveDocsUrl.🤖 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/default/src/lib/docs-url.ts` around lines 1 - 24, Update web/default/src/lib/docs-url.ts lines 1-24 to make resolveDocsUrl accept a supported locale, construct USER_GUIDE_DOCS_PATH with that locale, and apply an explicit fallback for unsupported or missing locales. Update web/default/src/hooks/use-top-nav-links.ts lines 59-91 to pass the resolved interface locale into resolveDocsUrl so the active language is preserved.web/default/src/docs/user-guide/zh/topup.mdx-14-34 (1)
14-34: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDocument Alipay when it is enabled.
The table and payment-selection instructions omit the Alipay payment flow introduced in this PR, leaving users without the new option. Add it alongside the other configurable providers.
🤖 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/default/src/docs/user-guide/zh/topup.mdx` around lines 14 - 34, Update the payment-method table and the payment-selection step in the Chinese top-up guide to include Alipay alongside EPay, Stripe, Creem, and Waffo. Keep the existing descriptions and payment flow unchanged while documenting Alipay wherever configurable providers are listed.web/default/src/routes/$locale/docs/guide/feature-guide/user/$slug.tsx-209-216 (1)
209-216: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winName the mobile language-menu trigger.
Below
sm, the visible label is hidden and the icon-only button has no accessible name. Addaria-label={label}to the trigger/button; mark the icon decorative witharia-hidden="true".🤖 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/default/src/routes/`$locale/docs/guide/feature-guide/user/$slug.tsx around lines 209 - 216, Update the language menu trigger in the DropdownMenuTrigger block to add aria-label={label} so the icon-only mobile button has an accessible name, and mark the Languages icon with aria-hidden="true" as decorative.Source: Coding guidelines
web/default/src/docs/user-guide/zh/subscription.mdx-16-22 (1)
16-22: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win补充完整的订阅购买流程。
英文版本包含选择套餐、点击购买、选择支付方式和确认支付等步骤;当前中文版本只说明浏览套餐。请补充购买与支付步骤,确保用户可以完成整个流程。
🤖 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/default/src/docs/user-guide/zh/subscription.mdx` around lines 16 - 22, 完善中文订阅指南中“订阅详情”下的购买流程,参考英文版本补充选择套餐、点击购买、选择支付方式和确认支付等步骤,并保留现有套餐信息说明及相关截图,使用户能够完成完整订阅流程。web/default/src/docs/user-guide/en/personal-setting.mdx-34-38 (1)
34-38: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winUse secure storage guidance for 2FA recovery codes.
Both localized guides recommend screenshots as a primary storage option. Replace that recommendation with password-manager or secure offline storage guidance.
web/default/src/docs/user-guide/en/personal-setting.mdx#L34-L38: update the English backup-code instructions.web/default/src/docs/user-guide/zh/personal-setting.mdx#L40-L45: update the Chinese backup-code instructions.🤖 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/default/src/docs/user-guide/en/personal-setting.mdx` around lines 34 - 38, Update the backup-code instructions in web/default/src/docs/user-guide/en/personal-setting.mdx at lines 34-38 and web/default/src/docs/user-guide/zh/personal-setting.mdx at lines 40-45 to recommend storing codes in a password manager or secure offline location instead of taking screenshots, while preserving the warning that codes are shown only once.web/default/src/docs/user-guide/en/pricing.mdx-17-20 (1)
17-20: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winUse one billing-unit convention across the top-up and pricing guides.
The pricing guide divides by 1000 for prices defined per 1K tokens, while the top-up guide omits that normalization. Define the multiplier unit and make both formulas identical.
web/default/src/docs/user-guide/en/pricing.mdx#L17-L20: retain or clarify the per-1K formula.web/default/src/docs/user-guide/en/topup.mdx#L6-L6: update the consumption formula to use the same unit convention.🤖 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/default/src/docs/user-guide/en/pricing.mdx` around lines 17 - 20, Use one clearly defined per-1K-token billing-unit convention in both guides. In web/default/src/docs/user-guide/en/pricing.mdx lines 17-20, retain or clarify the formula dividing token count by 1000; in web/default/src/docs/user-guide/en/topup.mdx line 6, update the consumption formula to match exactly, including the same normalization and unit wording.web/default/src/docs/user-guide/ja/api.mdx-55-55 (1)
55-55: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDocument Realtime as a WebSocket endpoint.
Replace the
GET /v1/realtimewording with aws:///wss:///v1/realtimeconnection URL and the requiredmodelquery/auth details, matching how the relay handles Realtime requests.🤖 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/default/src/docs/user-guide/ja/api.mdx` at line 55, Update the Realtime API row in the Japanese API documentation to describe a ws:// or wss:// /v1/realtime connection URL instead of GET, including the required model query parameter and authentication details consistent with the relay’s Realtime request handling.web/default/src/components/layout/components/footer.tsx-50-59 (1)
50-59: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMake guide links locale-aware.
Every fallback link targets
/zh/..., so English and Japanese visitors are sent to Chinese documentation. Derive these URLs from the active locale with a supported-locale fallback.Based on review context, localized guides are available in Chinese, English, and Japanese.
🤖 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/default/src/components/layout/components/footer.tsx` around lines 50 - 59, Update USER_GUIDE_DOCS to derive each documentation URL from the active locale instead of hardcoding /zh/. Support Chinese, English, and Japanese locale segments, and fall back to the supported default locale for unsupported or missing values while preserving the existing guide paths.web/default/src/features/home/components/hero-terminal-demo.tsx-171-210 (1)
171-210: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winLocalize the terminal UI labels.
routed,Request,Response,cost, andstream via sseremain English on every localized home page. Move these labels into translation keys.As per coding guidelines, “面向用户的文案必须使用 i18n”.
Also applies to: 218-225
🤖 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/default/src/features/home/components/hero-terminal-demo.tsx` around lines 171 - 210, The terminal demo’s user-facing labels remain hardcoded in English. Update the component containing `CodePanel` and the latency footer to obtain `routed`, `Request`, `Response`, `cost`, and `stream via sse` from the existing i18n translation mechanism, including the corresponding labels in the additional referenced section, and add the required translation keys for every supported locale.Source: Coding guidelines
web/default/src/components/language-switcher.tsx-63-77 (1)
63-77: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winCheck backend success before persisting the language change.
api.interceptors.responseshows non-skipBusiness{success: false}responses still resolve viareturn response, soapi.putcan reachsetUserand update localuser.settingeven when the backend rejected the language change. Only persist after an explicit success path, or make the PUT reject on business failures withoutskipBusinessError.🤖 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/default/src/components/language-switcher.tsx` around lines 63 - 77, The language change handler must not update local user settings when the backend returns a business-level failure. In the user branch around api.put, verify the PUT response represents explicit success before calling parseUserSetting and setUser, or make the request reject such failures through the existing interceptor behavior; preserve best-effort error handling and only persist language after confirmed success.web/default/src/i18n/locales/fr.json-114-114 (1)
114-114: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winNewly added French strings are missing accents/diacritics throughout the file.
Contrasting with pre-existing, correctly-accented French strings in the same file (e.g. Line 3788), the strings newly added in this PR systematically drop French diacritics and apostrophes — e.g. "modeles" instead of "modèles", "cle" instead of "clé", "generation" instead of "génération", "lusage"/"lIA"/"lupstream" instead of "l'usage"/"l'IA"/"l'upstream", "requetes"/"requete" instead of "requêtes"/"requête". This affects most of the ~50+ new entries in this file (representative examples cited above), degrading the French UI's professional quality.
Please run a proofreading/spell-check pass on the new entries before merge; happy to draft the corrected strings for the full list if useful.
Also applies to: 586-588, 909-909, 1001-1014, 1115-1115, 1563-1563, 1867-1867, 2071-2071, 2122-2122, 2148-2148, 2231-2231, 2366-2366, 2389-2389, 2442-2443, 2742-2750, 2794-2794, 2905-2907, 2974-2974, 3198-3198, 3455-3469, 3558-3558, 3685-3685, 3787-3790, 3933-3933, 3990-3990, 4174-4174, 4241-4241, 4319-4325, 4337-4338, 4391-4391, 4436-4442
🤖 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/default/src/i18n/locales/fr.json` at line 114, Proofread all newly added French entries in the locale resource, including the listed ranges, and restore correct French accents, diacritics, and apostrophes. Preserve each translation’s meaning and keys while correcting terms such as modèles, clé, génération, requêtes, and l’usage/l’IA/l’upstream throughout the affected entries.web/default/src/features/home/components/sections/operations-showcase.tsx-90-99 (1)
90-99: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winInconsistent
aria-hiddenon decorative icons.The icons in the "Go to Dashboard" link (Lines 94-97, 99) have no
aria-hidden, while the identical decorative icons insideentries.map(Lines 131, 144-148) correctly setaria-hidden='true'. Since the icon here is adjacent to descriptive text ("Go to Dashboard"), it is purely decorative and should be hidden from assistive tech for consistency.♿ Proposed fix
<HugeiconsIcon icon={DashboardBrowsingIcon} data-icon='inline-start' + aria-hidden='true' /> {t('Go to Dashboard')} - <HugeiconsIcon icon={ArrowRight01Icon} data-icon='inline-end' /> + <HugeiconsIcon icon={ArrowRight01Icon} data-icon='inline-end' aria-hidden='true' />As per coding guidelines: "装饰性图标使用
aria-hidden=\"true\",重要信息提供文本等价".🤖 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/default/src/features/home/components/sections/operations-showcase.tsx` around lines 90 - 99, Mark both HugeiconsIcon instances in the “Go to Dashboard” link with aria-hidden='true', matching the decorative icons used in entries.map while preserving the link’s existing descriptive text.Source: Coding guidelines
web/default/src/features/home/components/sections/operations-showcase.tsx-46-46 (1)
46-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse router links for these internal operations showcase targets
These destinations (
/keys,/usage-logs,/channels,/wallet,/dashboard) are internal TanStack Router routes. Plain anchors trigger full-page loads instead of client-side navigation; split theprotectedHreflogic into a helper and render those paths with@tanstack/react-router<Link>rather than<a href>.🤖 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/default/src/features/home/components/sections/operations-showcase.tsx` at line 46, Update the operations showcase navigation to use TanStack Router client-side links for the internal paths /keys, /usage-logs, /channels, /wallet, and /dashboard. Extract the existing protectedHref logic into a reusable helper, then render matching targets with `@tanstack/react-router` Link while preserving the current anchor behavior for non-internal destinations.web/default/src/i18n/locales/ru.json-2478-2478 (1)
2478-2478: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse a grammatical adjective for
Multimodal.
Мультимодальноis an adverb/short neuter form, while this source string is used as a standalone label. UseМультимодальныйto matchМультимодальный анализon Line 2479.🤖 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/default/src/i18n/locales/ru.json` at line 2478, Update the Russian translation for the “Multimodal” key to the grammatical adjective “Мультимодальный,” matching the wording used in the adjacent “Мультимодальный анализ” label.web/default/src/i18n/locales/ru.json-4442-4442 (1)
4442-4442: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPreserve the action in
View all models.
Все моделиmeans “All models” and omits “View”. UseПросмотреть все модели, consistent with the neighboringView all currently available modelstranslation.🤖 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/default/src/i18n/locales/ru.json` at line 4442, Update the `View all models` translation to `Просмотреть все модели`, preserving the action wording and matching the neighboring `View all currently available models` translation.web/default/src/i18n/locales/ru.json-3755-3755 (1)
3755-3755: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winTranslate
fallbackconsistently.
Умный fallbackmixes Russian and English, while nearby fallback labels useрезервный/резервирование. PreferУмное резервирование.🤖 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/default/src/i18n/locales/ru.json` at line 3755, Update the Russian translation for the “Smart fallback” key to use the consistent Russian term “Умное резервирование” instead of the English word “fallback”.web/default/src/i18n/locales/vi.json-114-114 (1)
114-114: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReplace the ASCII transliterations with reviewed Vietnamese translations.
Many new values omit Vietnamese diacritics (
ID ung dung,Suy luan,Dinh tuyen, etc.), and"Model marketplace": "Cho model"is mistranslated. This makes the Vietnamese UI look machine-generated and changes meaning in places. Please have the new entries reviewed by a Vietnamese speaker.Examples
- "Alipay App ID": "ID ung dung Alipay", + "Alipay App ID": "ID ứng dụng Alipay", - "Context": "Ngu canh", + "Context": "Ngữ cảnh", - "Model": "Model", + "Model": "Mô hình", - "Model marketplace": "Cho model", + "Model marketplace": "Thị trường mô hình",Also applies to: 252-258, 371-371, 415-416, 586-588, 708-708, 789-789, 817-819, 843-843, 909-909, 930-930, 940-940, 992-992, 1001-1004, 1014-1014, 1115-1115, 1563-1563, 1707-1720, 1779-1779, 1837-1837, 1867-1867, 1957-1957, 2071-2071, 2122-2122, 2148-2148, 2203-2203, 2220-2220, 2231-2231, 2265-2265, 2366-2366, 2389-2389, 2422-2422, 2442-2443, 2478-2479, 2742-2750, 2778-2782, 2794-2794, 2905-2907, 2974-2974, 3119-3119, 3164-3164, 3198-3198, 3239-3239, 3304-3304, 3379-3379, 3426-3426, 3442-3442, 3455-3469, 3558-3558, 3661-3661, 3685-3685, 3755-3755, 3787-3790, 3933-3933, 3990-3990, 4174-4174, 4241-4241, 4319-4325, 4337-4338, 4391-4391, 4436-4442, 4555-4555
🤖 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/default/src/i18n/locales/vi.json` at line 114, Review and replace the Vietnamese values for all newly added entries in vi.json, including the listed ranges, with natural, accurate translations using proper Vietnamese diacritics and terminology. Correct mistranslations such as “Model marketplace,” while preserving every existing key, placeholder, and JSON structure.Source: Learnings
web/default/src/i18n/locales/zh.json-1710-1711 (1)
1710-1711: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse routing terminology for “fallback”.
“备用” reads as generic backup and loses the request-routing/failover meaning. Use a consistent term such as “回退” or “故障转移” (for example, “智能故障转移”).
Also applies to: 3755-3755
🤖 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/default/src/i18n/locales/zh.json` around lines 1710 - 1711, Update the Chinese translations for the “fallback” and “Fallback tier” keys in the locale resource to use request-routing/failover terminology consistently, replacing the generic “备用” wording with the established “回退” or “故障转移” term, including the additional occurrence noted in the comment.web/default/src/i18n/locales/zh.json-3788-3788 (1)
3788-3788: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winUse a legally accurate Chinese term for “Merchant of Record”.
“登记商户” reads as merely registered merchants and does not capture the payment entity-of-record/compliance-liability role. Use an approved MoR term such as “商户记录主体(Merchant of Record)”, “记录商家(Merchant of Record)”, or “备案商家(Merchant of Record)”.
🤖 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/default/src/i18n/locales/zh.json` at line 3788, Update the translation value in the locale entry containing “Merchant of Record” to use an approved legally accurate Chinese term, such as “商户记录主体(Merchant of Record)”, while preserving the surrounding payment and compliance text.
🧹 Nitpick comments (13)
relay/channel/task/dreambrand/adaptor_test.go (1)
18-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winBoth new DreamBrand test suites use hand-rolled
if ... t.Fatalfinstead of testify. The shared root cause is that neither file adopts the repository's required assertion libraries; the table structures themselves are fine.
relay/channel/task/dreambrand/adaptor_test.go#L18-L32: convert the URL/payload/validation/response assertions torequirefor fatal checks andassertfor non-fatal field comparisons.relay/channel/dreambrand/adaptor_test.go#L22-L36: apply the same conversion across the conversion, polling, and normalization tests.As per coding guidelines, "New or substantially rewritten Go backend tests must use
testify/requirefor setup and fatal assertions andtestify/assertfor non-fatal checks".🤖 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 `@relay/channel/task/dreambrand/adaptor_test.go` around lines 18 - 32, Replace hand-rolled t.Fatalf assertions with testify/require for setup and fatal checks, and testify/assert for non-fatal comparisons in relay/channel/task/dreambrand/adaptor_test.go lines 18-32 and relay/channel/dreambrand/adaptor_test.go lines 22-36. Apply this consistently across the URL, payload, validation, response, conversion, polling, and normalization tests while preserving the existing table structures and test behavior.Source: Coding guidelines
relay/channel/dreambrand/adaptor.go (2)
212-250: 🧹 Nitpick | 🔵 TrivialLong-lived synchronous polling holds the request goroutine.
Default
pollTimeoutis 180s, so each pending image generation pins a request goroutine (and its upstream client connection) for up to three minutes. Consider surfacing the interval/timeout as channel settings and confirming your proxy/ingress read timeouts exceed it, otherwise clients see gateway timeouts while the poll keeps running.🤖 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 `@relay/channel/dreambrand/adaptor.go` around lines 212 - 250, Update the synchronous pollImageTask flow to avoid tying up the request for the full pollTimeout, exposing the polling interval and timeout through channel settings instead. Ensure the configured proxy/ingress read timeouts exceed the maximum polling duration so clients do not time out while polling continues.
404-478: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
parseUpstreamError,normalizeStatus, andbuildURLare duplicated inrelay/channel/task/dreambrand/adaptor.go.The task package (lines 103-105, 388-433, 499-516) re-implements the same three helpers with slightly different semantics (e.g.
errorReasonthere typescodeasstringwhile here it isjson.RawMessage). Sincerelay/channel/task/dreambrand/constants.goalready aliases into this package, exporting shared helpers here and reusing them would prevent the two copies from drifting further.🤖 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 `@relay/channel/dreambrand/adaptor.go` around lines 404 - 478, The helpers parseUpstreamError, normalizeStatus, and buildURL are duplicated in the task Dreambrand adaptor. Export these shared helpers from the channel Dreambrand package, preserving the existing response-code handling, status normalization, and URL construction semantics, then remove the task-package copies and update its callers to use the exported helpers through the existing constants.go aliasing.web/default/src/docs/user-guide.ts (3)
113-129: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRoute guide chrome through the i18n catalog.
USER_GUIDE_COPYand the hard-coded “Deeprouter Docs” label bypasst(), creating a separate localization mechanism for visible UI.
web/default/src/docs/user-guide.ts#L113-L129: replace display strings with translation keys or remove this UI-copy table.web/default/src/routes/$locale/docs/guide/feature-guide/user/$slug.tsx#L47-L70: useuseTranslation().t()for section, title, language, and brand text.🤖 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/default/src/docs/user-guide.ts` around lines 113 - 129, Replace the hard-coded USER_GUIDE_COPY values in web/default/src/docs/user-guide.ts lines 113-129 with translation keys or remove the table. In web/default/src/routes/$locale/docs/guide/feature-guide/user/$slug.tsx lines 47-70, use useTranslation().t() for the section, title, language, and “Deeprouter Docs” brand text, routing all visible guide chrome through the i18n catalog.Source: Coding guidelines
19-48: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy liftCode-split localized guide bodies.
These static imports ship every article for all three locales although the page renders one guide at a time. Keep navigation metadata synchronous, but dynamically import the selected locale/slug body.
🤖 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/default/src/docs/user-guide.ts` around lines 19 - 48, Update the localized guide loading in user-guide.ts so navigation metadata remains synchronously available while guide bodies are loaded via dynamic import only for the selected locale and slug. Replace the eager En*, Ja*, and Zh* markdown imports and connect the existing guide-selection flow to the corresponding lazy imports without changing navigation behavior.Source: Coding guidelines
71-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove the documentation asset origin to
VITE_configuration.This deployment-specific URL is hard-coded into the client bundle. Configure it through
.env(for example,VITE_DOCS_ASSET_BASE_URL) so self-hosted deployments can supply their own asset source.🤖 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/default/src/docs/user-guide.ts` around lines 71 - 72, Replace the hard-coded URL in the docs asset base configuration with the Vite-exposed environment variable VITE_DOCS_ASSET_BASE_URL, and update the related client configuration or environment template so deployments can provide this value through .env. Preserve the existing asset URL behavior by using the configured value wherever the documentation asset base URL is consumed.Source: Coding guidelines
controller/topup.go (1)
98-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the repeated "check-then-append pay method" pattern.
This is now the fourth copy (Stripe, WaffoPancake, Waffo, Alipay) of the identical "scan
payMethodsfor an existingtype, append if missing" loop. A small helper would remove the duplication and reduce the risk of copy/paste mistakes (e.g., wrongtypekey) as more providers are added.♻️ Proposed helper
+func appendPayMethodIfMissing(methods []map[string]string, methodType, name, color, minTopup string) []map[string]string { + for _, m := range methods { + if m["type"] == methodType { + return methods + } + } + return append(methods, map[string]string{ + "name": name, + "type": methodType, + "color": color, + "min_topup": minTopup, + }) +}- enableAlipay := isAlipayTopUpEnabled() - if enableAlipay { - hasAlipayOfficial := false - for _, method := range payMethods { - if method["type"] == model.PaymentMethodAlipayOfficial { - hasAlipayOfficial = true - break - } - } - if !hasAlipayOfficial { - payMethods = append(payMethods, map[string]string{ - "name": "Alipay Official", - "type": model.PaymentMethodAlipayOfficial, - "color": "rgba(var(--semi-blue-5), 1)", - "min_topup": strconv.Itoa(operation_setting.MinTopUp), - }) - } - } + enableAlipay := isAlipayTopUpEnabled() + if enableAlipay { + payMethods = appendPayMethodIfMissing(payMethods, model.PaymentMethodAlipayOfficial, "Alipay Official", "rgba(var(--semi-blue-5), 1)", strconv.Itoa(operation_setting.MinTopUp)) + }🤖 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 `@controller/topup.go` around lines 98 - 119, Extract the repeated pay-method existence check and conditional append into a shared helper, then update the Alipay flow and the existing Stripe, WaffoPancake, and Waffo flows to use it. The helper should scan payMethods by the "type" key and append the supplied method only when that type is absent, preserving current method data and ordering.router/relay-router.go (1)
189-198: 🚀 Performance & Scalability | 🔵 TrivialWiring looks correct; consider throughput protection for asset upload endpoints.
The new group correctly mirrors existing relay-router conventions (RouteTag, SystemPerformanceCheck, TokenAuth, Distribute). Since asset upload/audit requests can carry large payloads, ensure the downstream relay/billing pipeline enforces adequate size/throughput limits — this group has no explicit rate limiting beyond
SystemPerformanceCheckand token auth.🤖 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 `@router/relay-router.go` around lines 189 - 198, Update the assetAuditRouter group containing RelayAssetAuditUploadSync, RelayAssetAuditUploadAsync, and RelayAssetAuditTask to apply the established rate-limiting or request-size/throughput middleware used for comparable large-payload relay endpoints. Preserve the existing RouteTag, SystemPerformanceCheck, TokenAuth, and Distribute middleware while ensuring uploads are protected before reaching the downstream relay/billing pipeline.controller/topup_alipay.go (2)
171-190: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winConsider validating the notified paid amount against the order before crediting.
The handler credits quota using
topUp.Amountfrom our own record rather than the webhook'stotal_amount, so this isn't directly exploitable given the signature check, but there's no sanity check that Alipay's reported paid amount actually matchestopUp.Money. Adding that check would catch config drift or partial-payment edge cases before crediting quota.🤖 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 `@controller/topup_alipay.go` around lines 171 - 190, The pending-order success path should validate Alipay’s notified total_amount against the stored order amount before updating status or crediting quota. Add a comparison using the webhook payment value and topUp.Money, and reject/log mismatches with the existing failure response; only continue through topUp.Update and quota crediting when the amounts match.
107-108: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueDrop the GET Alipay notify route for page pay.
This topup flow uses
alipay.trade.page.paywithnotify_url, and Alipay async notifications for that flow arrive via POST. The GET handler will never process the configured async callback, sorouter/api-router.goline 80 should only be kept if another Alipay feature actually uses GET notifications.🤖 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 `@controller/topup_alipay.go` around lines 107 - 108, Remove the GET route registration for Alipay notifications in the router setup, while retaining the POST route used by the Alipay page-pay async callback. Verify references around AlipayNotify and the router registration, and only preserve a GET registration if another feature explicitly depends on GET notifications.web/default/src/features/home/components/sections/features.tsx (2)
128-168: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winFeature cards use plain
<a>tags for internal routes, forcing full-page reloads.
feature.href(e.g./pricing, orprotectedHref('/channels')when authenticated) is rendered via a raw<a href=...>rather than the router'sLink, so clicking any feature card triggers a full page reload instead of an SPA transition — even for the common authenticated case whereprotectedHrefreturns an unmodified internal path.hero.tsx'sHeroSidePanelhas the identical pattern.🤖 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/default/src/features/home/components/sections/features.tsx` around lines 128 - 168, Replace the raw anchor wrapping each feature card in the features section with the application router’s Link component, preserving feature.href and existing styling so internal routes use SPA navigation. Apply the same change to HeroSidePanel in hero.tsx, including protectedHref-generated internal paths, while retaining any required external-link behavior.
116-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHardcoded "Deeprouter" breaks configurable branding.
t('Why Deeprouter')bakes the brand name into the translation string, whereas other components in this cohort (public-header.tsx,auth-layout.tsx) derive the display name dynamically viauseSystemConfig()/customSiteName. This will show the wrong brand on deployments with a different configured site name.♻️ Use dynamic system name via interpolation
+import { useSystemConfig } from '`@/hooks/use-system-config`' ... export function Features(props: FeaturesProps) { const { t } = useTranslation() + const { systemName } = useSystemConfig() const protectedHref = (path: string) => ... - {t('Why Deeprouter')} + {t('Why {{systemName}}', { systemName })}🤖 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/default/src/features/home/components/sections/features.tsx` around lines 116 - 127, Update the features section component around the “Why Deeprouter” label to obtain the configured site name through useSystemConfig and its customSiteName value, then pass that name through the translation interpolation instead of hardcoding “Deeprouter”.web/default/src/features/home/components/sections/hero.tsx (1)
201-266: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
HeroSidePanelalso uses plain<a>tags for internal routes.Same pattern as
features.tsx:protectedHrefcan return an unmodified internal path (e.g./channels,/usage-logs) for authenticated users, yet the card is always rendered as a raw<a>, forcing a full page reload instead of an SPA transition through the router'sLink.🤖 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/default/src/features/home/components/sections/hero.tsx` around lines 201 - 266, Update HeroSidePanel to render internal preview links through the router’s Link component instead of raw <a> elements, while preserving protectedHref’s authenticated and sign-in redirect destinations. Pass each preview’s href to Link and retain the existing card styling, content, and behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0a7eda0e-f1cf-4fb6-9be9-605bea8be7eb
⛔ Files ignored due to path filters (3)
web/default/public/home/channel-health-preview.svgis excluded by!**/*.svgweb/default/public/home/model-routing-preview.svgis excluded by!**/*.svgweb/default/public/home/usage-logs-preview.svgis excluded by!**/*.svg
📒 Files selected for processing (135)
DockerfileDockerfile.devcommon/api_type.goconstant/api_type.goconstant/asset_audit.goconstant/channel.gocontroller/asset_audit.gocontroller/channel-test.gocontroller/misc.gocontroller/model.gocontroller/payment_webhook_availability.gocontroller/payment_webhook_availability_test.gocontroller/topup.gocontroller/topup_alipay.gocontroller/video_proxy.godocker-compose.dev.ymldocker-compose.ymlmiddleware/distributor.gomodel/option.gomodel/topup.gorelay/channel/dreambrand/adaptor.gorelay/channel/dreambrand/adaptor_test.gorelay/channel/dreambrand/constants.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/doubao/adaptor_test.gorelay/channel/task/doubao/constants.gorelay/channel/task/dreambrand/adaptor.gorelay/channel/task/dreambrand/adaptor_test.gorelay/channel/task/dreambrand/constants.gorelay/common/relay_info.gorelay/common/relay_utils.gorelay/relay_adaptor.gorelay/relay_adaptor_dreambrand_test.gorelay/relay_task.gorouter/api-router.gorouter/relay-router.goservice/alipay.gosetting/operation_setting/general_setting.gosetting/payment_alipay.gosetting/system_setting/theme.goweb/classic/src/components/layout/Footer.jsxweb/classic/src/constants/channel.constants.jsweb/classic/src/helpers/render.jsxweb/default/index.htmlweb/default/rsbuild.config.tsweb/default/scripts/sync-i18n.mjsweb/default/src/components/data-table/data-table-page.tsxweb/default/src/components/language-switcher.tsxweb/default/src/components/layout/components/footer.tsxweb/default/src/components/layout/components/header.tsxweb/default/src/components/layout/components/public-header.tsxweb/default/src/components/layout/components/section-page-layout.tsxweb/default/src/components/layout/components/top-nav.tsxweb/default/src/components/ui/card.tsxweb/default/src/docs/user-guide.tsweb/default/src/docs/user-guide/en/api.mdxweb/default/src/docs/user-guide/en/auth.mdxweb/default/src/docs/user-guide/en/chat-apps.mdxweb/default/src/docs/user-guide/en/log.mdxweb/default/src/docs/user-guide/en/meta.jsonweb/default/src/docs/user-guide/en/personal-setting.mdxweb/default/src/docs/user-guide/en/pricing.mdxweb/default/src/docs/user-guide/en/subscription.mdxweb/default/src/docs/user-guide/en/task.mdxweb/default/src/docs/user-guide/en/token.mdxweb/default/src/docs/user-guide/en/topup.mdxweb/default/src/docs/user-guide/ja/api.mdxweb/default/src/docs/user-guide/ja/auth.mdxweb/default/src/docs/user-guide/ja/chat-apps.mdxweb/default/src/docs/user-guide/ja/log.mdxweb/default/src/docs/user-guide/ja/meta.jsonweb/default/src/docs/user-guide/ja/personal-setting.mdxweb/default/src/docs/user-guide/ja/pricing.mdxweb/default/src/docs/user-guide/ja/subscription.mdxweb/default/src/docs/user-guide/ja/task.mdxweb/default/src/docs/user-guide/ja/token.mdxweb/default/src/docs/user-guide/ja/topup.mdxweb/default/src/docs/user-guide/zh/api.mdxweb/default/src/docs/user-guide/zh/auth.mdxweb/default/src/docs/user-guide/zh/chat-apps.mdxweb/default/src/docs/user-guide/zh/log.mdxweb/default/src/docs/user-guide/zh/meta.jsonweb/default/src/docs/user-guide/zh/personal-setting.mdxweb/default/src/docs/user-guide/zh/pricing.mdxweb/default/src/docs/user-guide/zh/subscription.mdxweb/default/src/docs/user-guide/zh/task.mdxweb/default/src/docs/user-guide/zh/token.mdxweb/default/src/docs/user-guide/zh/topup.mdxweb/default/src/env.d.tsweb/default/src/features/auth/auth-layout.tsxweb/default/src/features/auth/sign-in/index.tsxweb/default/src/features/channels/api.tsweb/default/src/features/channels/components/dialogs/codex-oauth-dialog.tsxweb/default/src/features/channels/components/drawers/channel-mutate-drawer.tsxweb/default/src/features/channels/constants.tsweb/default/src/features/channels/lib/channel-type-config.tsweb/default/src/features/channels/lib/channel-utils.tsweb/default/src/features/home/components/feature-item.tsxweb/default/src/features/home/components/hero-terminal-demo.tsxweb/default/src/features/home/components/index.tsweb/default/src/features/home/components/sections/cta.tsxweb/default/src/features/home/components/sections/featured-models.tsxweb/default/src/features/home/components/sections/features.tsxweb/default/src/features/home/components/sections/hero.tsxweb/default/src/features/home/components/sections/how-it-works.tsxweb/default/src/features/home/components/sections/operations-showcase.tsxweb/default/src/features/home/components/sections/stats.tsxweb/default/src/features/home/index.tsxweb/default/src/features/system-settings/billing/index.tsxweb/default/src/features/system-settings/billing/section-registry.tsxweb/default/src/features/system-settings/integrations/payment-settings-section.tsxweb/default/src/features/system-settings/types.tsweb/default/src/features/wallet/api.tsweb/default/src/features/wallet/components/recharge-form-card.tsxweb/default/src/features/wallet/components/subscription-plans-card.tsxweb/default/src/features/wallet/constants.tsweb/default/src/features/wallet/hooks/use-payment.tsweb/default/src/features/wallet/lib/billing.tsweb/default/src/features/wallet/lib/payment.tsweb/default/src/features/wallet/lib/ui.tsxweb/default/src/features/wallet/types.tsweb/default/src/hooks/use-top-nav-links.tsweb/default/src/i18n/locales/_reports/_sync-report.jsonweb/default/src/i18n/locales/en.jsonweb/default/src/i18n/locales/fr.jsonweb/default/src/i18n/locales/ja.jsonweb/default/src/i18n/locales/ru.jsonweb/default/src/i18n/locales/vi.jsonweb/default/src/i18n/locales/zh.jsonweb/default/src/lib/docs-url.tsweb/default/src/main.tsxweb/default/src/routeTree.gen.tsweb/default/src/routes/$locale/docs/guide/feature-guide/user/$slug.tsxweb/default/src/styles/index.cssweb/default/src/styles/theme.css
💤 Files with no reviewable changes (1)
- web/default/src/features/home/components/sections/cta.tsx
| if !priceData.FreeModel { | ||
| if apiErr := service.PreConsumeBilling(c, priceData.Quota, info); apiErr != nil { | ||
| c.JSON(apiErr.StatusCode, gin.H{"error": apiErr.ToOpenAIError()}) | ||
| return | ||
| } | ||
| } | ||
|
|
||
| resp, respBody, err := doAssetAuditRequest(c, method, upstreamPath) | ||
| if err != nil { | ||
| if info.Billing != nil { | ||
| info.Billing.Refund(c) | ||
| } | ||
| c.JSON(http.StatusInternalServerError, gin.H{ | ||
| "code": "do_request_failed", | ||
| "message": err.Error(), | ||
| }) | ||
| return | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| if resp.StatusCode >= http.StatusBadRequest { | ||
| if info.Billing != nil { | ||
| info.Billing.Refund(c) | ||
| } | ||
| service.IOCopyBytesGracefully(c, resp, respBody) | ||
| return | ||
| } | ||
|
|
||
| if err := service.SettleBilling(c, info, priceData.Quota); err != nil { | ||
| logger.LogError(c, fmt.Sprintf("asset audit settle billing failed: %v", err)) | ||
| } | ||
| recordAssetAuditConsumeLog(c, info, priceData.Quota, upstreamPath) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Preserve quota-saturation metadata before logging.
This flow never calls attachQuotaSaturation before Line 100 writes the consume log, and no clamp result is propagated into that log. Use the checked quota result and attach its saturation metadata before recording consumption.
As per coding guidelines, “Billing paths must use checked quota helpers, propagate clamp information, and call attachQuotaSaturation before writing consume or task logs so saturation is auditable.”
🤖 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 `@controller/asset_audit.go` around lines 69 - 100, Update the billing flow
around PreConsumeBilling and recordAssetAuditConsumeLog to use the checked quota
result, retain its clamp or saturation information, and call
attachQuotaSaturation before writing the consumption log. Ensure the resulting
metadata is propagated to recordAssetAuditConsumeLog while preserving the
existing refund and settlement behavior.
Source: Coding guidelines
|
|
||
| func AlipayNotify(c *gin.Context) { | ||
| if !isAlipayWebhookEnabled() { | ||
| logger.LogWarn(c.Request.Context(), fmt.Sprintf("Alipay webhook disabled path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Avoid logging raw Alipay webhook params (may contain buyer PII).
common.GetJsonString(params) is logged at Warn/Info level in several branches (signature-failure, ignored-status, order-not-found). Alipay's async-notify payload commonly includes buyer-identifying fields (e.g. buyer_logon_id, buyer_id). Persisting this verbatim in logs is a PII/compliance risk. Consider logging only the fields needed for troubleshooting (out_trade_no, trade_status, trade_no) rather than the full param map.
Also applies to: 137-141, 151-151, 161-161, 189-190
🤖 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 `@controller/topup_alipay.go` at line 109, Remove raw
common.GetJsonString(params) payloads from all Alipay webhook Warn/Info logs,
including the signature-failure, ignored-status, order-not-found, and related
branches in the webhook handler. Log only the troubleshooting fields
out_trade_no, trade_status, and trade_no, preserving the existing branch
behavior and context.
| func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (any, *types.NewAPIError) { | ||
| responseBody, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return nil, types.NewError(err, types.ErrorCodeReadResponseBodyFailed) | ||
| } | ||
| service.CloseResponseBodyGracefully(resp) | ||
|
|
||
| created, err := parseCreateResponse(responseBody) | ||
| if err != nil { | ||
| return nil, types.NewError(err, types.ErrorCodeBadResponseBody) | ||
| } | ||
| result := queryResponse{Status: created.Status, URL: created.URL, Created: created.Created} | ||
| if result.URL == "" && len(created.Data) > 0 { | ||
| result.URL = created.Data[0].URL | ||
| } | ||
|
|
||
| if result.URL == "" { | ||
| taskID := created.ID | ||
| if taskID == "" { | ||
| taskID = created.TaskID | ||
| } | ||
| if taskID == "" { | ||
| code, message := parseUpstreamError(responseBody) | ||
| if message == "" { | ||
| message = "DreamBrand image creation returned neither task ID nor URL" | ||
| } | ||
| if code == "" { | ||
| code = "invalid_response" | ||
| } | ||
| return nil, types.NewError(fmt.Errorf("%s", message), types.ErrorCode(code)) | ||
| } | ||
|
|
||
| result, _, err = a.pollImageTask(c.Request.Context(), info, taskID) | ||
| if err != nil { | ||
| if errors.Is(err, context.DeadlineExceeded) { | ||
| return nil, types.NewErrorWithStatusCode(err, types.ErrorCodeBadResponse, http.StatusGatewayTimeout, types.ErrOptionWithSkipRetry()) | ||
| } | ||
| return nil, types.NewError(err, types.ErrorCodeBadResponse, types.ErrOptionWithSkipRetry()) | ||
| } | ||
| } | ||
|
|
||
| if strings.TrimSpace(result.URL) == "" { | ||
| return nil, types.NewError(fmt.Errorf("DreamBrand image task succeeded without URL"), types.ErrorCodeBadResponse, types.ErrOptionWithSkipRetry()) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
DoResponse never inspects resp.StatusCode.
An upstream 401/429/5xx is treated like a success envelope: either parseCreateResponse fails (reported as ErrorCodeBadResponseBody, HTTP 500) or the error branch builds types.ErrorCode(code) from an arbitrary upstream code such as "30001". The real status is dropped, so rate limits and auth failures lose their retry/classification semantics downstream.
🛡️ Suggested status propagation
service.CloseResponseBodyGracefully(resp)
+ if resp.StatusCode != http.StatusOK {
+ code, message := parseUpstreamError(responseBody)
+ if message == "" {
+ message = fmt.Sprintf("DreamBrand returned HTTP %d: %s", resp.StatusCode, responseBody)
+ }
+ if code == "" {
+ code = string(types.ErrorCodeBadResponse)
+ }
+ return nil, types.NewErrorWithStatusCode(errors.New(message), types.ErrorCode(code), resp.StatusCode)
+ }
+
created, err := parseCreateResponse(responseBody)📝 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.
| func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (any, *types.NewAPIError) { | |
| responseBody, err := io.ReadAll(resp.Body) | |
| if err != nil { | |
| return nil, types.NewError(err, types.ErrorCodeReadResponseBodyFailed) | |
| } | |
| service.CloseResponseBodyGracefully(resp) | |
| created, err := parseCreateResponse(responseBody) | |
| if err != nil { | |
| return nil, types.NewError(err, types.ErrorCodeBadResponseBody) | |
| } | |
| result := queryResponse{Status: created.Status, URL: created.URL, Created: created.Created} | |
| if result.URL == "" && len(created.Data) > 0 { | |
| result.URL = created.Data[0].URL | |
| } | |
| if result.URL == "" { | |
| taskID := created.ID | |
| if taskID == "" { | |
| taskID = created.TaskID | |
| } | |
| if taskID == "" { | |
| code, message := parseUpstreamError(responseBody) | |
| if message == "" { | |
| message = "DreamBrand image creation returned neither task ID nor URL" | |
| } | |
| if code == "" { | |
| code = "invalid_response" | |
| } | |
| return nil, types.NewError(fmt.Errorf("%s", message), types.ErrorCode(code)) | |
| } | |
| result, _, err = a.pollImageTask(c.Request.Context(), info, taskID) | |
| if err != nil { | |
| if errors.Is(err, context.DeadlineExceeded) { | |
| return nil, types.NewErrorWithStatusCode(err, types.ErrorCodeBadResponse, http.StatusGatewayTimeout, types.ErrOptionWithSkipRetry()) | |
| } | |
| return nil, types.NewError(err, types.ErrorCodeBadResponse, types.ErrOptionWithSkipRetry()) | |
| } | |
| } | |
| if strings.TrimSpace(result.URL) == "" { | |
| return nil, types.NewError(fmt.Errorf("DreamBrand image task succeeded without URL"), types.ErrorCodeBadResponse, types.ErrOptionWithSkipRetry()) | |
| } | |
| func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (any, *types.NewAPIError) { | |
| responseBody, err := io.ReadAll(resp.Body) | |
| if err != nil { | |
| return nil, types.NewError(err, types.ErrorCodeReadResponseBodyFailed) | |
| } | |
| service.CloseResponseBodyGracefully(resp) | |
| if resp.StatusCode != http.StatusOK { | |
| code, message := parseUpstreamError(responseBody) | |
| if message == "" { | |
| message = fmt.Sprintf("DreamBrand returned HTTP %d: %s", resp.StatusCode, responseBody) | |
| } | |
| if code == "" { | |
| code = string(types.ErrorCodeBadResponse) | |
| } | |
| return nil, types.NewErrorWithStatusCode(errors.New(message), types.ErrorCode(code), resp.StatusCode) | |
| } | |
| created, err := parseCreateResponse(responseBody) | |
| if err != nil { | |
| return nil, types.NewError(err, types.ErrorCodeBadResponseBody) | |
| } | |
| result := queryResponse{Status: created.Status, URL: created.URL, Created: created.Created} | |
| if result.URL == "" && len(created.Data) > 0 { | |
| result.URL = created.Data[0].URL | |
| } | |
| if result.URL == "" { | |
| taskID := created.ID | |
| if taskID == "" { | |
| taskID = created.TaskID | |
| } | |
| if taskID == "" { | |
| code, message := parseUpstreamError(responseBody) | |
| if message == "" { | |
| message = "DreamBrand image creation returned neither task ID nor URL" | |
| } | |
| if code == "" { | |
| code = "invalid_response" | |
| } | |
| return nil, types.NewError(fmt.Errorf("%s", message), types.ErrorCode(code)) | |
| } | |
| result, _, err = a.pollImageTask(c.Request.Context(), info, taskID) | |
| if err != nil { | |
| if errors.Is(err, context.DeadlineExceeded) { | |
| return nil, types.NewErrorWithStatusCode(err, types.ErrorCodeBadResponse, http.StatusGatewayTimeout, types.ErrOptionWithSkipRetry()) | |
| } | |
| return nil, types.NewError(err, types.ErrorCodeBadResponse, types.ErrOptionWithSkipRetry()) | |
| } | |
| } | |
| if strings.TrimSpace(result.URL) == "" { | |
| return nil, types.NewError(fmt.Errorf("DreamBrand image task succeeded without URL"), types.ErrorCodeBadResponse, types.ErrOptionWithSkipRetry()) | |
| } |
🤖 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 `@relay/channel/dreambrand/adaptor.go` around lines 138 - 181, Update
DoResponse to inspect resp.StatusCode before treating the body as a successful
creation response, preserving the upstream HTTP status and retry/classification
semantics for 401, 429, and 5xx responses. Map non-success responses through the
existing API error/status handling rather than parseCreateResponse or arbitrary
types.ErrorCode(code), while leaving successful response parsing and polling
unchanged.
| func parseResponseTask(respBody []byte) (responseTask, error) { | ||
| var envelope responseTaskEnvelope | ||
| if err := common.Unmarshal(respBody, &envelope); err == nil && envelope.Data.ID != "" { | ||
| return envelope.Data, nil | ||
| } | ||
|
|
||
| var task responseTask | ||
| if err := common.Unmarshal(respBody, &task); err != nil { | ||
| return responseTask{}, err | ||
| } | ||
| return task, nil |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Reject failed task envelopes instead of treating them as active tasks.
An upstream {"code":"error","message":"..."} response decodes as an envelope with no task, then successfully decodes again into a zero-value responseTask. ParseTaskResult consequently reports it as in_progress, causing indefinite polling. Detect envelope responses and return an error for non-success codes or missing task data; add an error-envelope regression test.
🤖 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 `@relay/channel/task/doubao/adaptor.go` around lines 382 - 392, Update
parseResponseTask to distinguish decoded responseTaskEnvelope values from direct
task responses: when an envelope is detected, return envelope.Data only for a
successful code with a non-empty task ID, and return an error for non-success
codes or missing task data instead of falling back to a zero-value responseTask.
Add a regression test covering an error envelope and verify ParseTaskResult does
not report it as in_progress.
| type TaskAdaptor struct { | ||
| taskcommon.BaseBilling | ||
| baseURL string | ||
| apiKey string | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# BaseBilling defaults
fd -t f 'billing' relay/channel/task/taskcommon --exec cat -n
# Compare with an adaptor that prices duration
rg -nP -A20 'func \(a \*(TaskAdaptor|ZLHubTaskAdaptor)\) EstimateBilling' relay/channel/taskRepository: QuantumNous/new-api
Length of output: 7267
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== taskcommon billing files =="
fd -t f . relay/channel/task/taskcommon | sort
echo
echo "== BaseBilling implementation/usages =="
rg -n "type BaseBilling|func .*BaseBilling|EstimateBilling|AdjustBilling" relay/channel/task/taskcommon relay/task relay/common -g '*.go'
echo
echo "== dreambrand task adaptor relevant sections =="
wc -l relay/channel/task/dreambrand/adaptor.go
sed -n '1,180p' relay/channel/task/dreambrand/adaptor.go
sed -n '180,340p' relay/channel/task/dreambrand/adaptor.goRepository: QuantumNous/new-api
Length of output: 1234
Override EstimateBilling to include Dreambrand video ratios.
TaskAdaptor relies on taskcommon.BaseBilling.EstimateBilling, which documents that it returns nil and falls back to the base model price. Since Dreambrand accepts duration/resolution inputs that affect upstream cost, add the appropriate duration/resolution quotas via the quota helpers so pricing is applied correctly.
🤖 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 `@relay/channel/task/dreambrand/adaptor.go` around lines 97 - 101, Add a
TaskAdaptor.EstimateBilling override that uses the quota helpers to include
Dreambrand’s duration and resolution inputs when calculating video billing. Do
not rely on BaseBilling.EstimateBilling’s nil result; preserve the existing base
model price fallback while applying the appropriate duration/resolution quotas.
| func NormalizeDocsLink(link string) string { | ||
| link = strings.TrimSpace(link) | ||
| if link == "" || strings.Contains(link, "docs.newapi.pro") { | ||
| return DefaultDocsLink | ||
| } | ||
| return link | ||
| } | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Preserve the destination when normalizing legacy documentation links.
Any URL containing docs.newapi.pro is collapsed to the default Chinese authentication page, so legacy API, pricing, locale-specific, or custom documentation links are redirected to the wrong content. Restrict replacement to known default values or explicitly map old paths and locales to their new equivalents.
🤖 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 `@setting/operation_setting/general_setting.go` around lines 50 - 57, Update
NormalizeDocsLink so it no longer replaces every URL containing docs.newapi.pro
with DefaultDocsLink. Preserve legacy API, pricing, locale-specific, and custom
destinations by replacing only recognized old default values or explicitly
mapping known legacy paths/locales to their corresponding new links; keep
empty-link normalization unchanged.
| href='https://doc.deeprouterai.com/zh/docs/guide/feature-guide/user/auth' | ||
| target='_blank' | ||
| rel='noopener noreferrer' | ||
| className='!text-semi-color-text-1' | ||
| > | ||
| {t('关于项目')} | ||
| </a> | ||
| <a | ||
| href='https://docs.newapi.pro/support/community-interaction/' | ||
| href='https://doc.deeprouterai.com/zh/docs/guide/feature-guide/user/personal-setting' | ||
| target='_blank' | ||
| rel='noopener noreferrer' | ||
| className='!text-semi-color-text-1' | ||
| > | ||
| {t('联系我们')} | ||
| </a> | ||
| <a | ||
| href='https://docs.newapi.pro/wiki/features-introduction/' | ||
| href='https://doc.deeprouterai.com/zh/docs/guide/feature-guide/user/pricing' |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Derive the documentation locale instead of hard-coding /zh/.
This i18n-enabled footer now sends every user to Chinese guides, although the PR adds English and Japanese guide trees. Build these URLs from the active locale or a shared documentation URL helper.
Also applies to: 98-114
🤖 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/classic/src/components/layout/Footer.jsx` around lines 66 - 82, Update
the documentation links in the Footer component, including the related links
around the pricing entry, to derive the locale from the active i18n state or an
existing shared documentation URL helper instead of hard-coding `/zh/`. Preserve
each link’s existing guide path and behavior while routing users to the matching
localized documentation tree.
| request: `curl https://api.deeprouterai.com/v1/chat/completions \\ | ||
| -H "Authorization: Bearer dr-********" \\ | ||
| -H "Content-Type: application/json" \\ | ||
| -d '{ | ||
| "model": "openai/gpt-4o", | ||
| "messages": [{ "role": "user", "content": "Plan my launch" }] | ||
| }'`, | ||
| response: `{ | ||
| "choices": [{ "message": { "content": "Launch plan routed." } }], | ||
| "usage": { "total_tokens": 842 } | ||
| }`, | ||
| tokens: 842, | ||
| latency: 142, | ||
| cost: '$0.0021', | ||
| accent: 'emerald', | ||
| }, | ||
| { | ||
| id: 'responses', | ||
| label: 'Responses', | ||
| method: 'POST', | ||
| endpoint: '/v1/responses', | ||
| headers: ['"Authorization: Bearer sk-••••"'], | ||
| request: ['"model": "your-model",', '"input": "..."'], | ||
| response: [ | ||
| '{', | ||
| ' "output": [{ "type": "output_text", "text": <text> }],', | ||
| ' "usage": { "total_tokens": <tokens> }', | ||
| '}', | ||
| ], | ||
| responseHighlights: ['<text>', '<tokens>'], | ||
| tokens: 31, | ||
| id: 'claude', | ||
| label: 'Claude', | ||
| endpoint: '/v1/chat/completions', | ||
| request: `curl https://api.deeprouterai.com/v1/chat/completions \\ | ||
| -H "Authorization: Bearer dr-********" \\ | ||
| -H "Content-Type: application/json" \\ | ||
| -d '{ | ||
| "model": "anthropic/claude-3-5-sonnet", | ||
| "messages": [{ "role": "user", "content": "Review this spec" }] | ||
| }'`, | ||
| response: `{ | ||
| "choices": [{ "message": { "content": "Spec review routed." } }], | ||
| "usage": { "total_tokens": 1180 } | ||
| }`, | ||
| tokens: 1180, | ||
| latency: 168, | ||
| cost: '$0.0035', | ||
| accent: 'amber', | ||
| }, | ||
| { | ||
| id: 'claude', | ||
| label: 'Claude', | ||
| method: 'POST', | ||
| endpoint: '/v1/messages', | ||
| headers: ['"x-api-key: sk-••••"', '"anthropic-version: 2023-06-01"'], | ||
| request: [ | ||
| '"model": "your-model",', | ||
| '"max_tokens": 1024,', | ||
| '"messages": [', | ||
| ' { "role": "user", "content": "..." }', | ||
| ']', | ||
| ], | ||
| response: [ | ||
| '{', | ||
| ' "content": [{ "type": "text", "text": <text> }],', | ||
| ' "usage": { "input_tokens": <in>, "output_tokens": <out> }', | ||
| '}', | ||
| ], | ||
| responseHighlights: ['<text>', '<in>', '<out>'], | ||
| tokens: 29, | ||
| id: 'gemini', | ||
| label: 'Gemini', | ||
| endpoint: '/v1/chat/completions', | ||
| request: `curl https://api.deeprouterai.com/v1/chat/completions \\ | ||
| -H "Authorization: Bearer dr-********" \\ | ||
| -H "Content-Type: application/json" \\ | ||
| -d '{ | ||
| "model": "google/gemini-2.5-pro", | ||
| "messages": [{ "role": "user", "content": "Summarize this file" }] | ||
| }'`, | ||
| response: `{ | ||
| "choices": [{ "message": { "content": "Summary generated." } }], | ||
| "usage": { "total_tokens": 736 } | ||
| }`, | ||
| tokens: 736, | ||
| latency: 156, | ||
| cost: '$0.0018', | ||
| accent: 'blue', | ||
| }, | ||
| { | ||
| id: 'gemini', | ||
| label: 'Gemini', | ||
| method: 'POST', | ||
| endpoint: '/v1beta/models/{model}:generateContent', | ||
| headers: ['"x-goog-api-key: sk-••••"'], | ||
| request: [ | ||
| '"contents": [', | ||
| ' { "role": "user",', | ||
| ' "parts": [{ "text": "..." }] }', | ||
| ']', | ||
| ], | ||
| response: [ | ||
| '{', | ||
| ' "candidates": [{ "content": { "parts": [{ "text": <text> }] } }],', | ||
| ' "usageMetadata": { "totalTokenCount": <tokens> }', | ||
| '}', | ||
| ], | ||
| responseHighlights: ['<text>', '<tokens>'], | ||
| tokens: 25, | ||
| latency: 93, | ||
| id: 'seedance', | ||
| label: 'Seedance', | ||
| endpoint: '/v1/video/generations', | ||
| request: `curl https://api.deeprouterai.com/v1/video/generations \\ | ||
| -H "Authorization: Bearer dr-********" \\ | ||
| -H "Content-Type: application/json" \\ | ||
| -d '{ | ||
| "model": "doubao-seedance-2.0-fast", | ||
| "prompt": "Create a product video", | ||
| "metadata": { "duration": 5, "ratio": "16:9" } | ||
| }'`, |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not hard-code the API host in copyable requests.
A self-hosted user who replaces the placeholder token and runs these examples sends that token to api.deeprouterai.com, rather than their configured instance. Source the displayed base URL from the deployment’s VITE_ configuration.
As per coding guidelines, “环境变量使用 .env 并以 VITE_ 为前缀,代码中不得硬编码配置或密钥.”
🤖 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/default/src/features/home/components/hero-terminal-demo.tsx` around lines
48 - 115, The request examples in the demo data hard-code api.deeprouterai.com,
so update the surrounding hero-terminal demo configuration to derive the
displayed API base URL from the deployment’s VITE_ environment configuration and
interpolate it into every copyable request, including chat and video endpoints.
Remove the hard-coded host while preserving the existing paths and request
formatting.
Source: Coding guidelines
| <div className='grid gap-6 md:grid-cols-2'> | ||
| <FormField | ||
| control={form.control} | ||
| name='AlipayPrivateKey' | ||
| render={({ field }) => ( | ||
| <FormItem> | ||
| <FormLabel>{t('Alipay private key')}</FormLabel> | ||
| <FormControl> | ||
| <Textarea | ||
| rows={8} | ||
| placeholder={t('Paste your private key here')} | ||
| autoComplete='off' | ||
| {...field} | ||
| onChange={(event) => field.onChange(event.target.value)} | ||
| /> | ||
| </FormControl> | ||
| <FormDescription> | ||
| {t('RSA2 private key used to sign payment requests')} | ||
| </FormDescription> | ||
| <FormMessage /> | ||
| </FormItem> | ||
| )} | ||
| /> | ||
|
|
||
| <FormField | ||
| control={form.control} | ||
| name='AlipayPublicKey' | ||
| render={({ field }) => ( | ||
| <FormItem> | ||
| <FormLabel>{t('Alipay public key')}</FormLabel> | ||
| <FormControl> | ||
| <Textarea | ||
| rows={8} | ||
| placeholder={t('Paste Alipay public key here')} | ||
| autoComplete='off' | ||
| {...field} | ||
| onChange={(event) => field.onChange(event.target.value)} | ||
| /> | ||
| </FormControl> | ||
| <FormDescription> | ||
| {t('Alipay public key used to verify callbacks')} | ||
| </FormDescription> | ||
| <FormMessage /> | ||
| </FormItem> | ||
| )} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect how AlipayPrivateKey is populated/returned by the backend settings endpoint
fd -e go . -x rg -nP -C3 'AlipayPrivateKey' {}Repository: QuantumNous/new-api
Length of output: 2904
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate settings endpoint/type files =="
fd -e go . | rg 'setting|common|model|server|options'
echo
echo "== search settings CommonOptionMap and getter/update endpoints =="
rg -n -C 4 'CommonOptionMap|Get\(|Set\(|AlipayPrivateKey|AlipayAppId|AlipayPublicKey|AlipayGateway' -g '*.go' .Repository: QuantumNous/new-api
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files containing CommonOptionMap =="
rg -n -l -C 5 'CommonOptionMap\s*=' setting model common . --glob '*.go' | head -100
echo
echo "== file snippets around CommonOptionMap definitions =="
for f in $(rg -l 'CommonOptionMap\s*=' setting model common . --glob '*.go' | head -20); do
echo "--- $f"
rg -n -C 15 'CommonOptionMap|AlipayPrivateKey|AlipayAppId|AlipayGateway|AlipayPublicKey|EpayKey|StripeApiSecret|StripeWebhookSecret|CreemApiKey|CreemWebhookSecret' "$f" | head -220
done
echo
echo "== files containing GetOption / Get settings route patterns =="
rg -n -l -C 3 'func GetOption|GetOption|common\.OptionMap|/.*settings|Get\(\).*Option|Set\(\).*Option' --glob '*.go' . | head -100
echo
echo "== read likely model/common setting definitions =="
fd -e go model common setting | sed -n '1,200p'Repository: QuantumNous/new-api
Length of output: 196
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== all repository files containing CommonOptionMap =="
rg -n -C 12 'CommonOptionMap' . --glob '*.go' | sed -n '1,260p'
echo
echo "== all repository files containing GetOption / Get settings API =="
rg -n -C 12 'func GetOption|GetOption|GetSettings|Settings|system-settings|system_settings' --glob '*.go' . | sed -n '1,360p'Repository: QuantumNous/new-api
Length of output: 211
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repository files containing CommonOptionMap =="
grep -RIn -C 12 'CommonOptionMap' . --include='*.go' | sed -n '1,260p' || true
echo
echo "== repository files containing GetOption / settings API =="
grep -RIn -C 12 'func GetOption\|GetOption\|GetSettings\|Settings\|system-settings\|system_settings\|GetSettings\|POST .*settings\|GET .*settings' . --include='*.go' | sed -n '1,360p' || true
echo
echo "== candidate Go files in likely directories =="
find . -maxdepth 3 -type f \( -path './setting/*' -o -path './model/*' -o -path './common/*' \) -name '*.go' 2>/dev/null | sed -n '1,200p'Repository: QuantumNous/new-api
Length of output: 27523
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== model/option.go relevant sections =="
rg -n -C 20 'func .*Option|CommonOptionMap|OptionMap|AlipayPrivateKey|AlipayAppId|AlipayGateway|AlipayPublicKey|EpayKey|StripeApiSecret|StripeWebhookSecret|CreemApiKey|CreemWebhookSecret|GetOption|SetOption' model/option.go
echo
echo "== controller option implementation files =="
rg -n -l -C 4 'GetOptions|UpdateOption' --glob '*.go' .
echo
echo "== controller snippets =="
for f in $(rg -l 'GetOptions\(|UpdateOption\(' --glob '*.go' .); do
echo "--- $f"
rg -n -C 30 'GetOptions\(|UpdateOption\(' "$f" | sed -n '1,260p'
done
echo
echo "== frontend api call to /api/option GET path =="
rg -n -C 12 'api/option|/api/option|GET.*option|useQuery|queryKey.*option|AlipayPrivateKey|AlipayPublicKey' web --glob '*.ts' --glob '*.tsx' | sed -n '1,360p'Repository: QuantumNous/new-api
Length of output: 50375
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== common/options initialization for Alipay and sensitive options =="
rg -n -C 4 'Alipay(PrivateKey|PublicKey)|StripeApiSecret|StripeWebhookSecret|Creem(ApiKey|WebhookSecret)|Token|Secret|Key' --glob '*.go' common setting/model_setting common --glob '*.go' | sed -n '1,320p'
echo
echo "== exact OptionMap entries around Alipay in model/option.go =="
sed -n '73,98p' model/option.go | cat -n
echo
echo "== exact OptionMap exclusion logic in controller/option.go =="
sed -n '78,112p' controller/option.go | cat -n
echo
Echo "== frontend component around Alipay fields =="
sed -n '1300,1348p' web/default/src/features/system-settings/integrations/payment-settings-section.tsx | cat -nRepository: QuantumNous/new-api
Length of output: 19328
Treat Alipay keys as masked "update-only" secrets.
AlipayPrivateKey and AlipayPublicKey are stored in common.OptionMap and included in /api/option responses unless the key suffix is Key, so an AlipayPrivateKey value can be sent to the frontend. In this form it is displayed in plaintext via the Alipay private key section, and AlipayPublicKey is already bound to a plain Textarea. Keep both fields on the same "leave blank unless updating" pattern as the other secrets: omit/store a blank value by default, and only push a non-blank value when the admin explicitly changes it.
🤖 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/default/src/features/system-settings/integrations/payment-settings-section.tsx`
around lines 1306 - 1352, Update the AlipayPrivateKey and AlipayPublicKey fields
in the payment settings form to use the existing masked update-only secret
pattern: display blank values by default, avoid exposing stored credentials, and
submit/store them only when the administrator enters a non-blank replacement.
Reuse the established secret-field behavior and ensure both fields follow it
consistently.
| if (topupInfo.enable_alipay_topup) { | ||
| return PAYMENT_TYPES.ALIPAY_OFFICIAL | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Locate files =="
fd -a 'payment.ts|recharge-form-card.tsx' . | sed 's#^\./##'
echo "== payment.ts outline =="
file_payment="$(fd 'payment.ts' web/default/src/features/wallet/lib | head -n1)"
echo "PAYMENT_FILE=$file_payment"
ast-grep outline "$file_payment" || true
echo "== relevant payment.ts lines =="
wc -l "$file_payment"
sed -n '1,180p' "$file_payment" | cat -n
echo "== recharge form card outline/lines =="
file_form="$(fd 'recharge-form-card.tsx' web/default/src/features/wallet/components | head -n1)"
echo "FORM_FILE=$file_form"
wc -l "$file_form"
sed -n '1,220p' "$file_form" | cat -n
echo "== Search for pay_methods/enable_alipay_topup/usages =="
rg -n "enable_alipay_topup|enable_online_topup|pay_methods|ALIPAY_OFFICIAL|ALIPAY|payMethod|payment type|topupInfo" web/default/src/features/wallet -SRepository: QuantumNous/new-api
Length of output: 25713
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== use-topup-info parsing/payment methods slice =="
wc -l web/default/src/features/wallet/hooks/use-topup-info.ts
sed -n '60,210p' web/default/src/features/wallet/hooks/use-topup-info.ts | cat -n
echo "== wallet index default/payment selection =="
wc -l web/default/src/features/wallet/index.tsx
sed -n '120,230p' web/default/src/features/wallet/index.tsx | cat -n
sed -n '260,330p' web/default/src/features/wallet/index.tsx | cat -n
echo "== recharge form card payment method rendering =="
sed -n '290,430p' web/default/src/features/wallet/components/recharge-form-card.tsx | cat -n
echo "== PaymentMethod and topup-info related type definitions =="
sed -n '85,160p' web/default/src/features/wallet/types.ts | cat -n
sed -n '1,40p' web/default/src/features/wallet/lib/ui.tsx | cat -n
echo "== read-only structural check: exact default/payment-flow invariants =="
python3 - <<'PY'
from pathlib import Path
p = Path('web/default/src/features/wallet/lib/payment.ts')
s = p.read_text()
checks = {
"getDefaultPaymentType_prioritises_enable_alipay_topup": s.index('if (topupInfo.enable_alipay_topup)') < s.index('if (topupInfo.pay_methods?.length > 0)'),
"getDefaultPaymentType_returns_alipay_official_on_enable_flag": "'ALIPAY_OFFICIAL'" in s[s.index('if (topupInfo.enable_alipay_topup)'):s.index('if (topupInfo.pay_methods'))],
}
print(checks)
PYRepository: QuantumNous/new-api
Length of output: 25740
Align Alipay enablement with an actual renderable method.
enable_alipay_topup now makes the recharge form visible and selects ALIPAY_OFFICIAL first, but payment buttons are only rendered from pay_methods or Waffo methods. If official Alipay is not present in pay_methods and no Waffo methods are available, the UI shows an empty/Payments unavailable state while pre-selecting ALIPAY_OFFICIAL, so the selection can never be confirmed.
- Either render an explicit official Alipay option or require an actual available method when this flag is used.
📍 Affects 2 files
web/default/src/features/wallet/lib/payment.ts#L105-L107(this comment)web/default/src/features/wallet/components/recharge-form-card.tsx#L127-L129
🤖 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/default/src/features/wallet/lib/payment.ts` around lines 105 - 107, Align
the enable_alipay_topup handling in payment.ts and recharge-form-card.tsx so
selecting PAYMENT_TYPES.ALIPAY_OFFICIAL is only possible when a corresponding
renderable payment option exists. Either add an explicit official Alipay button
to the recharge form or require the method to be present in pay_methods/Waffo
methods before showing the form and selecting it; update both referenced sites
consistently.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
relay/common/relay_utils.go (1)
81-119: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winMultipart submissions silently drop resolution/ratio/generate_audio/watermark/content.
isKnownTaskFieldnow marksresolution,ratio,generate_audio,watermark, andcontentas known (excluding them from the genericreq.Metadatafallback loop), butvalidateMultipartTaskRequestnever reads these keys intoreq.Resolution/req.Ratio/req.GenerateAudio/req.Watermark/req.Content. Previously these unknown keys landed inreq.Metadata, which downstream code (e.g. DreamBrand'sresolveVideoMetadata) falls back to; now, for multipart requests, the values are captured nowhere — neither the typed field norMetadata— so they are silently lost.🐛 Proposed fix to bind the new fields for multipart requests
if images := formData["images"]; len(images) > 0 { req.Images = images } + if v := formData.Get("resolution"); v != "" { + req.Resolution = &v + } + if v := formData.Get("ratio"); v != "" { + req.Ratio = &v + } + if v := formData.Get("generate_audio"); v != "" { + if b, err := strconv.ParseBool(v); err == nil { + req.GenerateAudio = &b + } + } + if v := formData.Get("watermark"); v != "" { + if b, err := strconv.ParseBool(v); err == nil { + req.Watermark = &b + } + } + for key, values := range formData {Also applies to: 184-201
🤖 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 `@relay/common/relay_utils.go` around lines 81 - 119, Update validateMultipartTaskRequest to explicitly read the known multipart form keys resolution, ratio, generate_audio, watermark, and content into the corresponding TaskSubmitReq fields before the generic metadata loop. Preserve the existing parsing behavior and ensure these values are no longer dropped when isKnownTaskField excludes them from req.Metadata.
🤖 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.
Outside diff comments:
In `@relay/common/relay_utils.go`:
- Around line 81-119: Update validateMultipartTaskRequest to explicitly read the
known multipart form keys resolution, ratio, generate_audio, watermark, and
content into the corresponding TaskSubmitReq fields before the generic metadata
loop. Preserve the existing parsing behavior and ensure these values are no
longer dropped when isKnownTaskField excludes them from req.Metadata.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d3ec826a-a4ad-4990-a9ab-6263b78ae10c
📒 Files selected for processing (6)
relay/channel/dreambrand/adaptor.gorelay/channel/dreambrand/adaptor_test.gorelay/channel/task/dreambrand/adaptor.gorelay/channel/task/dreambrand/adaptor_test.gorelay/common/relay_info.gorelay/common/relay_utils.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
relay/channel/dreambrand/adaptor_test.go (1)
38-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
testify/requirefor fatal assertions.This new backend test uses
t.Fatalf, but repository guidelines requiretestify/requirefor fatal assertions. Replace these comparisons withrequire.Equal(...)and importtestify/require.🤖 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 `@relay/channel/dreambrand/adaptor_test.go` around lines 38 - 48, Update TestInitUsesImagePollingDefaults to import testify/require and replace both t.Fatalf comparison blocks with require.Equal assertions for pollInterval and pollTimeout, preserving the existing expected values and failure behavior.Source: Coding guidelines
🤖 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.
Nitpick comments:
In `@relay/channel/dreambrand/adaptor_test.go`:
- Around line 38-48: Update TestInitUsesImagePollingDefaults to import
testify/require and replace both t.Fatalf comparison blocks with require.Equal
assertions for pollInterval and pollTimeout, preserving the existing expected
values and failure behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1decb60e-bf25-44cd-ac96-4f36cbd89b92
📒 Files selected for processing (2)
relay/channel/dreambrand/adaptor.gorelay/channel/dreambrand/adaptor_test.go
Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)
Summary by CodeRabbit