对即梦2.0完美支持 - #4872
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds async video adaptors (XAI/ApiMart/ApiWenhao), VolcEngine TTS v3 and Doubao content support, Gemini preview-TTS defaults, content-capable TaskSubmitReq, expanded billing/usage logging and token backfill, affiliate invitee APIs, frontend billing UI for per-second pricing, docs/examples, and dependency adjustments. ChangesPlatform & Adaptor Support
Task schema, validation, polling, and billing
Affiliate invitee features
API response, docs, frontend, deps
Sequence Diagram(s)sequenceDiagram
actor Client
participant Gateway as Relay Controller
participant Adaptor as Channel Adaptor
participant Upstream as Upstream Provider
participant Billing as Billing Service
Client->>Gateway: POST /v1/task (content/video request)
Gateway->>Adaptor: BuildRequest + credentials
Adaptor->>Upstream: Create task (Bearer/JSON)
Upstream-->>Adaptor: 2xx (task_id)
Adaptor-->>Gateway: relay create response (task_id, publicTaskID)
Gateway->>Billing: LogTaskConsumption(relayInfo, publicTaskID) [after TaskID set]
Client->>Gateway: Poll task status
Gateway->>Adaptor: FetchTask (task_id + upstream_model)
Adaptor->>Upstream: Query task
Upstream-->>Adaptor: Task result (status, url, usage)
Adaptor-->>Gateway: TaskInfo (merged usage)
Gateway->>Billing: ReportTaskUsageToConsumeLog(task, usage)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches🧪 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 |
|
即梦2.0是视频行业领先模型,支持 |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
relay/common/relay_info.go (1)
4-4:⚠️ Potential issue | 🔴 CriticalRemove
encoding/jsonimport or add a wrapper type tocommon/json.go.The
encoding/jsonimport at line 4 violates the coding guideline that states "Do NOT directly import or callencoding/jsonin business code." While the file correctly usescommon.Marshal()andcommon.Unmarshal()for all JSON operations, it importsencoding/jsononly to usejson.RawMessageas a field type (lines 700–701).Replace
json.RawMessagewith[]bytefor those fields, or create a type alias incommon/json.go(e.g.,type RawMessage = encoding/json.RawMessage) and import that instead.🤖 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_info.go` at line 4, This file imports encoding/json only to use json.RawMessage for struct fields; remove the direct encoding/json import and either (A) change those fields that reference json.RawMessage to use []byte instead (search for occurrences of json.RawMessage in relay_info.go around the struct field declarations) and keep using common.Marshal/common.Unmarshal for JSON ops, or (B) add a safe alias in common/json.go (e.g., type RawMessage = encoding/json.RawMessage) and replace references to json.RawMessage with common.RawMessage, then remove the direct encoding/json import from relay_info.go; ensure all references to json.RawMessage are updated and tests/compilation pass.service/task_polling.go (1)
552-565:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPer-call tasks skip usage backfill entirely.
The early return on
PerCallBillingexits beforeReportTaskUsageToConsumeLog, so any successful per-call task that reportsusagenever writes its prompt/completion tokens. That defeats the new “write usage regardless of quota delta” path for this billing mode.Suggested fix
func settleTaskBillingOnComplete(ctx context.Context, adaptor TaskPollingAdaptor, task *model.Task, taskResult *relaycommon.TaskInfo) { // 0. 按次计费的任务不做差额结算 - if bc := task.PrivateData.BillingContext; bc != nil && bc.PerCallBilling { + if bc := task.PrivateData.BillingContext; bc != nil && bc.PerCallBilling { logger.LogInfo(ctx, fmt.Sprintf("任务 %s 按次计费,跳过差额结算", task.TaskID)) - return - } - // 1. 优先让 adaptor 决定最终额度 - if actualQuota := adaptor.AdjustBillingOnComplete(task, taskResult); actualQuota > 0 { + } else if actualQuota := adaptor.AdjustBillingOnComplete(task, taskResult); actualQuota > 0 { RecalculateTaskQuota(ctx, task, actualQuota, "adaptor计费调整", taskResult) } else if taskResult != nil && taskResult.TotalTokens > 0 { // 2. 回退到 token 重算 RecalculateTaskQuotaByTokens(ctx, task, taskResult) } // 3. 将 usage 写回消费日志(与额度差额是否为零无关) ReportTaskUsageToConsumeLog(task, taskResult) }🤖 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 `@service/task_polling.go` around lines 552 - 565, The early return for per-call billing in the completion billing path causes ReportTaskUsageToConsumeLog not to run, so per-call tasks never write usage; remove the return and instead let the PerCallBilling branch skip quota recalculation but still call ReportTaskUsageToConsumeLog. Concretely, in the block that checks task.PrivateData.BillingContext and bc.PerCallBilling, stop returning immediately — call ReportTaskUsageToConsumeLog(task, taskResult) after skipping RecalculateTaskQuota/RecalculateTaskQuotaByTokens, and keep the existing adaptor.AdjustBillingOnComplete call path unchanged for non-per-call cases.controller/relay.go (1)
566-590:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPersist the task before finalizing billing/logging.
This block still settles quota and writes the consume log before
task.Insert()succeeds. If the insert fails, the request leaves behind charged quota plus apublic_task_idconsume log, but no task row for polling or later reconciliation.🤖 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/relay.go` around lines 566 - 590, The code currently calls service.SettleBilling and service.LogTaskConsumption before persisting the task, which can leave charged quota and a consume log if Insert() fails; change the order so you create the task with model.InitTask, populate task.PrivateData/.../Action, then call task.Insert() first and only after insert succeeds call service.SettleBilling(c, relayInfo, result.Quota) and service.LogTaskConsumption(c, relayInfo, task.TaskID); ensure that on insertErr you do not call SettleBilling or LogTaskConsumption and return/handle the error (or attempt appropriate rollback) so billing and logs only occur for persisted tasks.
🧹 Nitpick comments (3)
relay/channel/task/doubao/adaptor.go (3)
345-357: ⚡ Quick winValidate mutually exclusive duration and ratio fields.
The code allows both
req.Secondsandreq.Durationto be set, withDurationsilently overwritingSeconds. Similarly, bothreq.Ratioandreq.AspectRatiocan overwrite each other. Users who set both fields may be confused when one is ignored.Consider validating that only one field from each pair is set, or documenting the precedence in API documentation.
🛡️ Suggested validation
if sec, _ := strconv.Atoi(req.Seconds); sec > 0 { + if req.Duration > 0 { + return nil, errors.New("cannot specify both 'seconds' and 'duration' fields") + } r.Duration = lo.ToPtr(dto.IntValue(sec)) } if req.Duration > 0 { r.Duration = lo.ToPtr(dto.IntValue(req.Duration)) } if req.Ratio != "" { + if req.AspectRatio != "" { + return nil, errors.New("cannot specify both 'ratio' and 'aspect_ratio' fields") + } r.Ratio = req.Ratio } if req.AspectRatio != "" { r.Ratio = req.AspectRatio }🤖 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 345 - 357, The current assignment logic silently overwrites fields: r.Duration can be set from both req.Seconds and req.Duration, and r.Ratio from req.Ratio and req.AspectRatio; update the code that sets r.Duration and r.Ratio to validate mutual exclusivity and fail fast: detect when both req.Seconds and req.Duration are provided and return an error (or validation response) instead of overwriting, and likewise detect when both req.Ratio and req.AspectRatio are provided and return an error; implement these checks immediately before the existing assignments around r.Duration, r.Ratio (the block that parses strconv.Atoi(req.Seconds) and assigns lo.ToPtr(dto.IntValue(req.Duration)) and assigns r.Ratio), so callers get a clear validation error rather than silent precedence.
333-337: ⚡ Quick winClarify or remove redundant text item filtering.
Line 333 uses
lo.Rejectto remove all existing text-type items before appending the prompt as a new text item. In the current fallback branch,r.Contentonly contains image items at this point, so the reject operation is a no-op.This pattern is confusing and could hide bugs if the code is refactored. If the intent is to ensure only one text item exists, consider documenting this or restructuring the logic.
♻️ Suggested clarification
- r.Content = lo.Reject(r.Content, func(c ContentItem, _ int) bool { return c.Type == "text" }) + // Ensure prompt text is the only text item (remove any existing text items) + r.Content = lo.Reject(r.Content, func(c ContentItem, _ int) bool { return c.Type == "text" }) r.Content = append(r.Content, ContentItem{ Type: "text", Text: req.Prompt, })Or remove the reject if it's truly unnecessary:
- r.Content = lo.Reject(r.Content, func(c ContentItem, _ int) bool { return c.Type == "text" }) r.Content = append(r.Content, ContentItem{ Type: "text", Text: req.Prompt, })🤖 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 333 - 337, The lo.Reject call that removes Type=="text" from r.Content before appending a new text ContentItem is redundant in the current fallback where r.Content contains only images; update the code around r.Content to either (a) remove the lo.Reject line entirely if you guarantee this branch only ever holds images, or (b) make the intent explicit by replacing any existing text items instead of blindly rejecting (e.g., filter and replace text items) and add a short comment explaining "ensure exactly one text item" so future refactors don't reintroduce confusion; target the r.Content manipulation (lo.Reject, ContentItem{Type:"text", Text:req.Prompt}) in adaptor.go when making the change.
27-29: ⚡ Quick winConsider more robust URL pattern matching.
The
isVolcEngineOfficialfunction uses string prefix matching to detect official VolcEngine URLs. This approach is fragile:
- Future official domains won't be recognized automatically
- Unofficial domains starting with
ark.orvisual.would be incorrectly classifiedConsider using an allowlist of complete domain names or a configurable pattern.
♻️ Suggested improvement
func isVolcEngineOfficial(baseURL string) bool { - return strings.HasPrefix(baseURL, "https://ark.") || strings.HasPrefix(baseURL, "https://visual.") + // Check against known official VolcEngine domains + officialDomains := []string{ + "https://ark.cn-beijing.volces.com", + "https://visual.volcengineapi.com", + // Add other official domains as needed + } + for _, domain := range officialDomains { + if strings.HasPrefix(baseURL, domain) { + return true + } + } + return false }🤖 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 27 - 29, The isVolcEngineOfficial function currently uses naive prefix checks on baseURL; instead parse baseURL (e.g., using url.Parse) to extract the hostname and match it against a maintained allowlist or configurable pattern of full domain names (or strict suffixes like ".volcengine.net") rather than prefix strings; update isVolcEngineOfficial to validate the parsed hostname against that allowlist/config (or a compiled regex) so only exact/approved hostnames (not any URL starting with "https://ark." or "https://visual.") are treated as official.
🤖 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 @.gitignore:
- Line 34: Remove the placeholder literal pattern "..." from the .gitignore
file; locate the entry that contains just three dots and delete that line so
only intentional ignore patterns remain (no code changes required beyond
removing the "..." line).
- Line 33: The .gitignore entry for token_estimator_test.go is incorrectly
hiding a real test file from version control. Remove that ignore rule from
.gitignore so the token_estimator_test.go test can be committed and picked up by
CI; verify there are no other test-related patterns in .gitignore that would
also exclude intended test files.
In `@relay/channel/task/doubao/adaptor.go`:
- Around line 289-317: The loop that builds ContentItem from req.Content
(creating ContentItem and MediaURL and appending to r.Content) must validate
each content item before appending: ensure each item is a map[string]interface{}
(or handle its actual type), and if keys like
"image_url"/"video_url"/"audio_url" exist they must be maps containing a string
"url"; if any expected nested type assertion fails, either skip the malformed
item or return an explicit error to the caller instead of appending a partially
populated ContentItem. Update the code around the ContentItem construction (the
loop over req.Content, the ContentItem and MediaURL usage, and the r.Content
append) to perform these checks and log/propagate a clear error describing which
field was invalid (e.g., include the item index and offending key) rather than
silently ignoring type assertion failures.
In `@relay/channel/task/xai/adaptor.go`:
- Line 5: Remove the direct encoding/json import and replace all usages of
json.RawMessage with []byte (e.g., change fields or local vars typed as
json.RawMessage to []byte) in relay/channel/task/xai/adaptor.go (the occurrence
around the json.RawMessage usage); keep using common.Unmarshal() for decoding
and ensure no other direct calls to encoding/json remain—delete the import line
referencing "encoding/json" and update any function signatures or struct fields
that referenced json.RawMessage to use []byte instead.
In `@web/src/components/topup/RechargeCard.jsx`:
- Around line 571-583: The hardcoded insecure URL in the RechargeCard component
should be removed and replaced with the existing configurable topUpLink and
i18n; update the anchor that currently uses 'http://83zi.com/faka.html' to use
the component's topUpLink variable (the same source used in the block around
lines 612-623) and wrap the link text with t('购买兑换码') for translation, ensure
the link uses HTTPS if provided and fall back to hiding the anchor when
topUpLink is not set so admin-configurable settings control the external
destination; locate the anchor in RechargeCard.jsx and swap to topUpLink usage
and translated text accordingly.
---
Outside diff comments:
In `@controller/relay.go`:
- Around line 566-590: The code currently calls service.SettleBilling and
service.LogTaskConsumption before persisting the task, which can leave charged
quota and a consume log if Insert() fails; change the order so you create the
task with model.InitTask, populate task.PrivateData/.../Action, then call
task.Insert() first and only after insert succeeds call service.SettleBilling(c,
relayInfo, result.Quota) and service.LogTaskConsumption(c, relayInfo,
task.TaskID); ensure that on insertErr you do not call SettleBilling or
LogTaskConsumption and return/handle the error (or attempt appropriate rollback)
so billing and logs only occur for persisted tasks.
In `@relay/common/relay_info.go`:
- Line 4: This file imports encoding/json only to use json.RawMessage for struct
fields; remove the direct encoding/json import and either (A) change those
fields that reference json.RawMessage to use []byte instead (search for
occurrences of json.RawMessage in relay_info.go around the struct field
declarations) and keep using common.Marshal/common.Unmarshal for JSON ops, or
(B) add a safe alias in common/json.go (e.g., type RawMessage =
encoding/json.RawMessage) and replace references to json.RawMessage with
common.RawMessage, then remove the direct encoding/json import from
relay_info.go; ensure all references to json.RawMessage are updated and
tests/compilation pass.
In `@service/task_polling.go`:
- Around line 552-565: The early return for per-call billing in the completion
billing path causes ReportTaskUsageToConsumeLog not to run, so per-call tasks
never write usage; remove the return and instead let the PerCallBilling branch
skip quota recalculation but still call ReportTaskUsageToConsumeLog. Concretely,
in the block that checks task.PrivateData.BillingContext and bc.PerCallBilling,
stop returning immediately — call ReportTaskUsageToConsumeLog(task, taskResult)
after skipping RecalculateTaskQuota/RecalculateTaskQuotaByTokens, and keep the
existing adaptor.AdjustBillingOnComplete call path unchanged for non-per-call
cases.
---
Nitpick comments:
In `@relay/channel/task/doubao/adaptor.go`:
- Around line 345-357: The current assignment logic silently overwrites fields:
r.Duration can be set from both req.Seconds and req.Duration, and r.Ratio from
req.Ratio and req.AspectRatio; update the code that sets r.Duration and r.Ratio
to validate mutual exclusivity and fail fast: detect when both req.Seconds and
req.Duration are provided and return an error (or validation response) instead
of overwriting, and likewise detect when both req.Ratio and req.AspectRatio are
provided and return an error; implement these checks immediately before the
existing assignments around r.Duration, r.Ratio (the block that parses
strconv.Atoi(req.Seconds) and assigns lo.ToPtr(dto.IntValue(req.Duration)) and
assigns r.Ratio), so callers get a clear validation error rather than silent
precedence.
- Around line 333-337: The lo.Reject call that removes Type=="text" from
r.Content before appending a new text ContentItem is redundant in the current
fallback where r.Content contains only images; update the code around r.Content
to either (a) remove the lo.Reject line entirely if you guarantee this branch
only ever holds images, or (b) make the intent explicit by replacing any
existing text items instead of blindly rejecting (e.g., filter and replace text
items) and add a short comment explaining "ensure exactly one text item" so
future refactors don't reintroduce confusion; target the r.Content manipulation
(lo.Reject, ContentItem{Type:"text", Text:req.Prompt}) in adaptor.go when making
the change.
- Around line 27-29: The isVolcEngineOfficial function currently uses naive
prefix checks on baseURL; instead parse baseURL (e.g., using url.Parse) to
extract the hostname and match it against a maintained allowlist or configurable
pattern of full domain names (or strict suffixes like ".volcengine.net") rather
than prefix strings; update isVolcEngineOfficial to validate the parsed hostname
against that allowlist/config (or a compiled regex) so only exact/approved
hostnames (not any URL starting with "https://ark." or "https://visual.") are
treated as official.
🪄 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: 162fcdfa-b02b-433e-8f61-9d7b05f7199e
⛔ Files ignored due to path filters (2)
go.sumis excluded by!**/*.sum视频生成对接文档_技术版.docis excluded by!**/*.doc
📒 Files selected for processing (26)
.gitignorecontroller/channel-test.gocontroller/relay.gocontroller/token.gocontroller/user.gogo.modmodel/log.gomodel/user.gorelay/channel/gemini/adaptor.gorelay/channel/gemini/constant.gorelay/channel/gemini/relay-gemini.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/xai/adaptor.gorelay/channel/volcengine/constants.gorelay/common/relay_info.gorelay/common/relay_utils.gorelay/relay_adaptor.gorelay/relay_task.gorouter/api-router.goservice/task_billing.goservice/task_billing_test.goservice/task_polling.goweb/src/components/topup/InvitationCard.jsxweb/src/components/topup/RechargeCard.jsxweb/src/components/topup/index.jsxweb/src/components/topup/modals/AffInviteesModal.jsx
|
|
||
| import ( | ||
| "bytes" | ||
| "encoding/json" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify no direct encoding/json import remains in this adaptor.
rg -n --type=go '"encoding/json"|json\.RawMessage' relay/channel/task/xai/adaptor.goRepository: QuantumNous/new-api
Length of output: 123
🏁 Script executed:
#!/bin/bash
# Check context around line 283 and verify if there are other encoding/json usages
echo "=== Context around line 283 (20 lines) ==="
sed -n '275,295p' relay/channel/task/xai/adaptor.go
echo -e "\n=== Check for all encoding/json patterns in file ==="
rg -n --type=go 'encoding/json|json\.' relay/channel/task/xai/adaptor.go | head -20
echo -e "\n=== Check file imports ==="
head -30 relay/channel/task/xai/adaptor.goRepository: QuantumNous/new-api
Length of output: 1590
Remove direct encoding/json import and replace json.RawMessage with []byte.
The file imports encoding/json at line 5 and uses json.RawMessage at line 283. This violates the project's Go JSON requirement: all business code must use wrapper functions from common/json.go and must not directly import or call encoding/json.
The fix is straightforward: remove the import and replace json.RawMessage with []byte (which is functionally equivalent). All JSON operations in the code already use common.Unmarshal().
Proposed patch
import (
"bytes"
- "encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
@@
var wrap struct {
- Data json.RawMessage `json:"data"`
+ Data []byte `json:"data"`
}📝 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.
| "encoding/json" | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "io" | |
| "net/http" | |
| "net/url" | |
| "strings" | |
| ... | |
| ) | |
| ... | |
| var wrap struct { | |
| Data []byte `json:"data"` | |
| } |
🤖 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/xai/adaptor.go` at line 5, Remove the direct encoding/json
import and replace all usages of json.RawMessage with []byte (e.g., change
fields or local vars typed as json.RawMessage to []byte) in
relay/channel/task/xai/adaptor.go (the occurrence around the json.RawMessage
usage); keep using common.Unmarshal() for decoding and ensure no other direct
calls to encoding/json remain—delete the import line referencing "encoding/json"
and update any function signatures or struct fields that referenced
json.RawMessage to use []byte instead.
| <div className='flex items-center gap-2'> | ||
| <Text type='tertiary' strong> | ||
| {t('兑换码充值')} | ||
| </Text> | ||
| <a | ||
| href='http://83zi.com/faka.html' | ||
| target='_blank' | ||
| rel='noopener noreferrer' | ||
| className='text-blue-500 hover:text-blue-600' | ||
| > | ||
| 购买兑换码 | ||
| </a> | ||
| </div> |
There was a problem hiding this comment.
Remove hardcoded external URL or make it configurable.
Line 576 contains a hardcoded insecure external URL (http://83zi.com/faka.html). This introduces several critical issues:
- Security: Uses insecure HTTP instead of HTTPS
- Maintainability: Hardcoded URLs cannot be changed without code deployment
- Configuration conflict: The codebase already supports a configurable
topUpLink(see lines 612-623), making this redundant and inconsistent - Internationalization: Link text "购买兑换码" is not wrapped in
t()for translation - External dependency: Links to what appears to be a third-party commercial service without admin control
Recommended solution: Remove this hardcoded link and rely on the existing configurable topUpLink system, or introduce a new backend setting if a separate redemption purchase link is needed.
🔒 Proposed fix to remove hardcoded URL
<Card
className='!rounded-xl w-full'
title={
- <div className='flex items-center gap-2'>
- <Text type='tertiary' strong>
- {t('兑换码充值')}
- </Text>
- <a
- href='http://83zi.com/faka.html'
- target='_blank'
- rel='noopener noreferrer'
- className='text-blue-500 hover:text-blue-600'
- >
- 购买兑换码
- </a>
- </div>
+ <Text type='tertiary' strong>
+ {t('兑换码充值')}
+ </Text>
}
>📝 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.
| <div className='flex items-center gap-2'> | |
| <Text type='tertiary' strong> | |
| {t('兑换码充值')} | |
| </Text> | |
| <a | |
| href='http://83zi.com/faka.html' | |
| target='_blank' | |
| rel='noopener noreferrer' | |
| className='text-blue-500 hover:text-blue-600' | |
| > | |
| 购买兑换码 | |
| </a> | |
| </div> | |
| <Text type='tertiary' strong> | |
| {t('兑换码充值')} | |
| </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/src/components/topup/RechargeCard.jsx` around lines 571 - 583, The
hardcoded insecure URL in the RechargeCard component should be removed and
replaced with the existing configurable topUpLink and i18n; update the anchor
that currently uses 'http://83zi.com/faka.html' to use the component's topUpLink
variable (the same source used in the block around lines 612-623) and wrap the
link text with t('购买兑换码') for translation, ensure the link uses HTTPS if
provided and fall back to hiding the anchor when topUpLink is not set so
admin-configurable settings control the external destination; locate the anchor
in RechargeCard.jsx and swap to topUpLink usage and translated text accordingly.
# Conflicts: # .gitignore # model/log.go # web/classic/src/components/topup/InvitationCard.jsx # web/classic/src/components/topup/RechargeCard.jsx # web/classic/src/components/topup/index.jsx # web/classic/src/components/topup/modals/AffInviteesModal.jsx
There was a problem hiding this comment.
🧹 Nitpick comments (1)
model/log.go (1)
322-355: 🏗️ Heavy liftReconsider the Limit(80) logic—unnecessary fallback logs can be eliminated with a time-based window.
The function has a fallback mechanism that creates a new consume log when
TryPatchTaskConsumeLogTokensreturns false. However, for tasks with multi-hour delays (e.g., video processing), the hardcodedLimit(80)can cause the original submission log to fall out of the lookup window before the patch is applied. This forces an unnecessary fallback log to be created withis_task_usage_only=true, resulting in two log entries per task instead of a single patched log.While both logs are counted in token aggregations (the
is_task_usage_onlyflag is not filtered bySumUsedToken), eliminating unnecessary fallback logs improves log cleanliness and query efficiency.A time-based window (e.g., 24 hours) is a straightforward improvement:
♻️ Time-bounded lookup
- var logs []Log - err := LOG_DB.Where("user_id = ? AND type = ?", userId, LogTypeConsume). - Order("id desc").Limit(80).Find(&logs).Error + var logs []Log + // Limit lookback to last 24h to avoid unnecessary fallback logs for multi-hour tasks + since := time.Now().Add(-24 * time.Hour).Unix() + err := LOG_DB.Where("user_id = ? AND type = ? AND created_at >= ?", userId, LogTypeConsume, since). + Order("id desc").Limit(500).Find(&logs).Error if err != nil { return false }For longer-running tasks or higher confidence, consider indexing
public_task_idas a dedicated column to enable direct O(1) lookup instead of scanning parsed JSON.🤖 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 `@model/log.go` around lines 322 - 355, TryPatchTaskConsumeLogTokens currently restricts the search with Limit(80), which can miss original task logs for long-running tasks; replace the fixed row limit with a time-bounded WHERE clause (e.g., filter Log.CreatedAt >= now()-24h or a configurable window) when querying LOG_DB in TryPatchTaskConsumeLogTokens so the lookup finds older matching logs, and optionally add/document an index or dedicated column for public_task_id to allow direct lookup instead of parsing Other JSON; ensure the query still filters by user_id and type = LogTypeConsume and preserve the later Updates(...) call on the matched logs.
🤖 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 `@model/log.go`:
- Around line 322-355: TryPatchTaskConsumeLogTokens currently restricts the
search with Limit(80), which can miss original task logs for long-running tasks;
replace the fixed row limit with a time-bounded WHERE clause (e.g., filter
Log.CreatedAt >= now()-24h or a configurable window) when querying LOG_DB in
TryPatchTaskConsumeLogTokens so the lookup finds older matching logs, and
optionally add/document an index or dedicated column for public_task_id to allow
direct lookup instead of parsing Other JSON; ensure the query still filters by
user_id and type = LogTypeConsume and preserve the later Updates(...) call on
the matched logs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: af8d84d3-cf25-4925-b995-36978ab3a012
⛔ Files ignored due to path filters (2)
web/classic/bun.lockis excluded by!**/*.lockweb/default/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
model/log.gomodel/user.goservice/task_billing.goweb/classic/src/components/table/usage-logs/UsageLogsColumnDefs.jsxweb/default/src/features/usage-logs/components/columns/common-logs-columns.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- model/user.go
- service/task_billing.go
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
web/classic/src/i18n/i18n.js (1)
48-53: ⚡ Quick winReconsider fallback order for non‑Chinese users.
For
en,fr,ru,ja,viusers thedefaultchain falls back tozh-CNbeforeen. Practically this means an English user encountering a missing key inen.jsonwill see Chinese text rather than English, which is worse than falling back to the most-complete English bundle. Consider keeping the Chinese-first chain only forzh-*and using an English-first chain elsewhere.🩹 Suggested fix
fallbackLng: { 'zh-TW': ['zh-CN', 'en'], 'zh-CN': ['en'], - default: ['zh-CN', 'en'], + default: ['en', 'zh-CN'], },🤖 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/i18n/i18n.js` around lines 48 - 53, The current i18n fallbackLng defaults to ['zh-CN','en'] causing non‑Chinese locales to fall back to Chinese before English; update the fallbackLng mapping so Chinese-first behavior is only for Chinese locales (e.g., keep 'zh-TW': ['zh-CN','en'] and 'zh-CN': ['en'] or similar), and make the global/default fallback English-first (e.g., default: ['en','zh-CN']) so locales like 'en','fr','ru','ja','vi' will prefer English before Chinese; update the fallbackLng object accordingly.
🤖 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 `@relay/channel/volcengine/tts_v3.go`:
- Around line 66-77: The function mapEncodingToTTSV3Format currently maps
"ogg_opus" to "mp3" (making that case effectively dead code) which causes
callers requesting opus to receive MP3 streams; update mapEncodingToTTSV3Format
so that the "ogg_opus" case returns the correct upstream token ("opus")
supported by Volcengine OpenSpeech TTS v3 (or alternatively return an
error/empty string if you prefer to explicitly reject it), and ensure the switch
still falls back to "mp3" only in the default branch; refer to
mapEncodingToTTSV3Format to locate and fix the mapping.
In `@web/classic/src/index.jsx`:
- Around line 48-52: The current React.useMemo that sets semiLocale based on
i18n.language is too simplistic and forces zh_CN for any non-"en" value and
fails to handle regional codes like "en-US"; update the logic in the semiLocale
useMemo to use a lookup map (e.g., a const locales = { en: en_GB, 'en-US':
en_GB, 'zh-CN': zh_CN, 'zh-TW': zh_TW, 'fr': fr_FR, 'ja': ja_JP, 'ru': ru_RU,
'vi': vi_VN, ... } or match by normalized prefix) and select the appropriate
Semi UI locale by checking i18n.language (normalize to lower-case and prefer
exact key then prefix match like startsWith) instead of strict equality; restore
the missing locale entries (zh_TW, fr_FR, ja_JP, ru_RU, vi_VN) referenced from
`@douyinfe/semi-ui` locale source and use i18n.language as the lookup key for
semiLocale.
---
Nitpick comments:
In `@web/classic/src/i18n/i18n.js`:
- Around line 48-53: The current i18n fallbackLng defaults to ['zh-CN','en']
causing non‑Chinese locales to fall back to Chinese before English; update the
fallbackLng mapping so Chinese-first behavior is only for Chinese locales (e.g.,
keep 'zh-TW': ['zh-CN','en'] and 'zh-CN': ['en'] or similar), and make the
global/default fallback English-first (e.g., default: ['en','zh-CN']) so locales
like 'en','fr','ru','ja','vi' will prefer English before Chinese; update the
fallbackLng object accordingly.
🪄 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: 7107a6c7-4644-4fe8-97ac-82b9d61a3e60
📒 Files selected for processing (7)
constant/channel.godocs/examples/relay-tts-speech-test.mjsdocs/local-relay-tts-test.mdrelay/channel/volcengine/adaptor.gorelay/channel/volcengine/tts_v3.goweb/classic/src/i18n/i18n.jsweb/classic/src/index.jsx
✅ Files skipped from review due to trivial changes (1)
- docs/local-relay-tts-test.md
| func mapEncodingToTTSV3Format(encoding string) string { | ||
| switch encoding { | ||
| case "ogg_opus": | ||
| return "mp3" | ||
| case "pcm": | ||
| return "pcm" | ||
| case "wav": | ||
| return "wav" | ||
| default: | ||
| return "mp3" | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Volcengine OpenSpeech TTS HTTP v3 unidirectional supported audio_params format values opus
💡 Result:
For the Volcengine (BytePlus) OpenSpeech TTS HTTP v3 API, the audio_params configuration supports opus as a valid audio format [1]. In the context of Volcengine's speech synthesis services, when configuring audio output parameters (often within an audio_params object in the request body), opus is a supported encoding format [1]. Implementations typically specify this within the format field [1]. For example, configurations often use format: "opus" alongside other parameters such as sample_rate (e.g., 24000) and channels (e.g., 1) [1]. While specific documentation for the v3 HTTP interface may vary by service cluster, opus is widely recognized and used across Volcengine's speech synthesis and recognition APIs as a standard compressed audio format [2][3][1][4]. If you are integrating this into a system, ensure your request body correctly structures the audio_params object to include the format key set to "opus" [1].
Citations:
- 1: https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/main/xiaozhi-server/config.yaml
- 2: https://www.volcengine.com/docs/6561/1631584
- 3: https://www.volcengine.com/docs/6561/1354868
- 4: https://www.volcengine.com/docs/6561/1257584
🏁 Script executed:
# Examine the file around the flagged lines and surrounding context
cat -n relay/channel/volcengine/tts_v3.go | head -150Repository: QuantumNous/new-api
Length of output: 4863
🏁 Script executed:
# Search for calls to mapEncodingToTTSV3Format to understand how it's used
rg "mapEncodingToTTSV3Format" relay/channel/volcengine/Repository: QuantumNous/new-api
Length of output: 327
🏁 Script executed:
# Search for mapEncoding function to understand encoding transformations
rg "mapEncoding|func.*[Ee]ncoding" relay/channel/volcengine/ -A 3Repository: QuantumNous/new-api
Length of output: 4129
🏁 Script executed:
# Check if there are other files in the channel directory that handle encoding
fd "\.go$" relay/channel/ --exec grep -l "mapEncoding\|encoding.*format" {} \;Repository: QuantumNous/new-api
Length of output: 166
🏁 Script executed:
# Check the responseFormatToEncodingMap and mapEncoding function in tts.go
cat -n relay/channel/volcengine/tts.go | grep -A 20 "responseFormatToEncodingMap\|func mapEncoding"Repository: QuantumNous/new-api
Length of output: 1553
🏁 Script executed:
# Check what format is expected by Volcengine for opus in the request structure
rg "format.*opus|opus.*format" relay/channel/volcengine/ -iRepository: QuantumNous/new-api
Length of output: 45
🏁 Script executed:
# Look at more context from the web search result about Volcengine format values
# Also check if there's any documentation in the repo about supported formats
fd "volcengine|bytedance" -t f relay/channel/ | head -20Repository: QuantumNous/new-api
Length of output: 45
🏁 Script executed:
# Check if there are any comments or tests about format mappings
rg "mp3|pcm|wav|opus" relay/channel/volcengine/ --context 2Repository: QuantumNous/new-api
Length of output: 2573
🏁 Script executed:
# Verify the exact format string Volcengine v3 expects for opus
# Check if there's any documentation or comments in the codebase
rg "opus|format.*value|audio_params" relay/channel/volcengine/tts_v3.go -B 2 -A 2 | head -40Repository: QuantumNous/new-api
Length of output: 359
🏁 Script executed:
# Check if there are any issues or comments indicating this is intentional
rg "TODO|FIXME|BUG|XXX" relay/channel/volcengine/ | grep -i "opus\|format\|encoding"Repository: QuantumNous/new-api
Length of output: 45
Fix the silent format fallback for ogg_opus in Volcengine OpenSpeech TTS v3.
Volcengine's OpenSpeech TTS v3 API supports opus as a valid audio format. Currently, mapEncodingToTTSV3Format() silently converts ogg_opus requests to mp3 (returning the same value as the default case, making the case dead code). This breaks the API contract: a caller requesting opus gets an MP3 stream with mismatched Content-Type: audio/ogg headers. Either return an error early to inform callers that this encoding is unsupported, or map ogg_opus to the correct upstream format token that Volcengine v3 accepts.
🤖 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/volcengine/tts_v3.go` around lines 66 - 77, The function
mapEncodingToTTSV3Format currently maps "ogg_opus" to "mp3" (making that case
effectively dead code) which causes callers requesting opus to receive MP3
streams; update mapEncodingToTTSV3Format so that the "ogg_opus" case returns the
correct upstream token ("opus") supported by Volcengine OpenSpeech TTS v3 (or
alternatively return an error/empty string if you prefer to explicitly reject
it), and ensure the switch still falls back to "mp3" only in the default branch;
refer to mapEncodingToTTSV3Format to locate and fix the mapping.
| const semiLocale = React.useMemo(() => { | ||
| const lng = i18n.language || ''; | ||
| if (lng === 'en') return en_GB; | ||
| return zh_CN; | ||
| }, [i18n.language]); |
There was a problem hiding this comment.
Locale regression for non-Chinese / non-English UI users.
The simplified mapping forces zh_CN for every language other than en (e.g. zh-TW, fr, ru, ja, vi). Users with those UI languages will now see Semi UI components (pagination, date picker, modals, etc.) rendered in Simplified Chinese while the rest of the app is in their selected language. The earlier object-lookup form most likely covered more locales — please confirm and restore the missing mappings (e.g. zh_TW, fr_FR, ja_JP, ru_RU, vi_VN from @douyinfe/semi-ui/lib/es/locale/source/*).
Additionally, lng === 'en' is strict equality. If the language detector ever resolves to a regional code like en-US, this branch will not match. Prefer a normalized prefix check (or an explicit map).
🩹 Suggested fix
import zh_CN from '@douyinfe/semi-ui/lib/es/locale/source/zh_CN';
import en_GB from '@douyinfe/semi-ui/lib/es/locale/source/en_GB';
+import zh_TW from '@douyinfe/semi-ui/lib/es/locale/source/zh_TW';
+import fr_FR from '@douyinfe/semi-ui/lib/es/locale/source/fr_FR';
+import ja_JP from '@douyinfe/semi-ui/lib/es/locale/source/ja_JP';
+import ru_RU from '@douyinfe/semi-ui/lib/es/locale/source/ru_RU';
+import vi_VN from '@douyinfe/semi-ui/lib/es/locale/source/vi_VN';
@@
- const semiLocale = React.useMemo(() => {
- const lng = i18n.language || '';
- if (lng === 'en') return en_GB;
- return zh_CN;
- }, [i18n.language]);
+ const semiLocale = React.useMemo(() => {
+ const lng = (i18n.language || '').toLowerCase();
+ if (lng.startsWith('zh-tw') || lng.startsWith('zh-hk') || lng.startsWith('zh-hant')) return zh_TW;
+ if (lng.startsWith('zh')) return zh_CN;
+ if (lng.startsWith('en')) return en_GB;
+ if (lng.startsWith('fr')) return fr_FR;
+ if (lng.startsWith('ja')) return ja_JP;
+ if (lng.startsWith('ru')) return ru_RU;
+ if (lng.startsWith('vi')) return vi_VN;
+ return en_GB;
+ }, [i18n.language]);🤖 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/index.jsx` around lines 48 - 52, The current React.useMemo
that sets semiLocale based on i18n.language is too simplistic and forces zh_CN
for any non-"en" value and fails to handle regional codes like "en-US"; update
the logic in the semiLocale useMemo to use a lookup map (e.g., a const locales =
{ en: en_GB, 'en-US': en_GB, 'zh-CN': zh_CN, 'zh-TW': zh_TW, 'fr': fr_FR, 'ja':
ja_JP, 'ru': ru_RU, 'vi': vi_VN, ... } or match by normalized prefix) and select
the appropriate Semi UI locale by checking i18n.language (normalize to
lower-case and prefer exact key then prefix match like startsWith) instead of
strict equality; restore the missing locale entries (zh_TW, fr_FR, ja_JP, ru_RU,
vi_VN) referenced from `@douyinfe/semi-ui` locale source and use i18n.language as
the lookup key for semiLocale.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
relay/channel/task/apiwenhao/adaptor.go (1)
258-273: 💤 Low valueRemove redundant
extractMediaURLcall.The
extractMediaURLcall at line 266 is redundant—it was already called at line 259 with the samerawinput. If line 259 returned an empty URL, line 266 will also return empty, making the inner block unreachable.♻️ Suggested simplification
case resTypeSuccess: if url := extractMediaURL(raw); url != "" { taskResult.Status = model.TaskStatusSuccess taskResult.Progress = "100%" taskResult.Url = url return &taskResult, nil } - if status := strings.ToLower(strings.TrimSpace(gjson.Get(raw, "data.result.status").String())); status == "completed" { - if url := extractMediaURL(raw); url != "" { - taskResult.Status = model.TaskStatusSuccess - taskResult.Progress = "100%" - taskResult.Url = url - return &taskResult, nil - } - } + // res_type=success but no media URL yet - fall through to in-progress handling }🤖 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/apiwenhao/adaptor.go` around lines 258 - 273, In the resTypeSuccess branch replace the duplicate extractMediaURL(raw) call by calling extractMediaURL(raw) once into a local variable (e.g., url := extractMediaURL(raw)) at the top of that case and reuse that url when setting taskResult.Status, Progress and Url; update the inner status=="completed" check to reference the same url variable instead of calling extractMediaURL again (symbols: extractMediaURL, resTypeSuccess case, taskResult).
🤖 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 `@controller/task_video.go`:
- Around line 83-87: The call to adaptor.FetchTask currently always passes
task.Properties.UpstreamModelName into BuildTaskFetchBody which can be empty for
older tasks; update the code around the adaptor.FetchTask / BuildTaskFetchBody
invocation to use a backward-compatible fallback: if
task.Properties.UpstreamModelName is empty, pass the saved request model (e.g.,
task.RequestModel or the persisted request-model field on the task) instead, and
if neither exists pass an empty/omitted model value so BuildTaskFetchBody
generates a payload without the model field. Ensure the conditional checks
reference task.Properties.UpstreamModelName and the saved request model symbol
you have (e.g., task.RequestModel) so the fetch payload is complete for older
queued jobs.
In `@setting/billing_setting/upstream_cost_multiplier_test.go`:
- Around line 7-15: The test mutates global map
billingSetting.UpstreamCostMultiplier and currently calls delete(...) at the end
which will be skipped on failure; fix by registering a t.Cleanup callback
immediately after setting billingSetting.UpstreamCostMultiplier["test-model"] =
7.3 (use t.Cleanup to delete that key) so the map entry is removed regardless of
test failures, then proceed to call ResolveUpstreamCostMultiplier and the
assertions as before.
---
Nitpick comments:
In `@relay/channel/task/apiwenhao/adaptor.go`:
- Around line 258-273: In the resTypeSuccess branch replace the duplicate
extractMediaURL(raw) call by calling extractMediaURL(raw) once into a local
variable (e.g., url := extractMediaURL(raw)) at the top of that case and reuse
that url when setting taskResult.Status, Progress and Url; update the inner
status=="completed" check to reference the same url variable instead of calling
extractMediaURL again (symbols: extractMediaURL, resTypeSuccess case,
taskResult).
🪄 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: 18031daf-b5af-413e-bc15-a192bb771489
📒 Files selected for processing (40)
constant/channel.gocontroller/ratio_sync.gocontroller/relay.gocontroller/task_video.gomodel/pricing.gomodel/task.gorelay/channel/task/apimart/adaptor.gorelay/channel/task/apimart/adaptor_test.gorelay/channel/task/apiwenhao/adaptor.gorelay/channel/task/apiwenhao/adaptor_test.gorelay/channel/task/taskcommon/usd_billing.gorelay/channel/task/taskcommon/usd_billing_test.gorelay/common/relay_info.gorelay/common/relay_utils.gorelay/common/task_submit_image.gorelay/common/task_submit_image_test.gorelay/relay_adaptor.goservice/task_billing.goservice/task_billing_test.goservice/task_polling.gosetting/billing_setting/tiered_billing.gosetting/billing_setting/upstream_cost_multiplier_test.goweb/classic/src/components/table/model-pricing/modal/ModelDetailSideSheet.jsxweb/classic/src/components/table/model-pricing/modal/components/ModelPricingTable.jsxweb/classic/src/components/table/model-pricing/view/card/PricingCardView.jsxweb/classic/src/components/table/model-pricing/view/table/PricingTableColumns.jsxweb/classic/src/constants/channel.constants.jsweb/classic/src/helpers/render.jsxweb/classic/src/helpers/utils.jsxweb/classic/src/pages/Setting/Ratio/components/ModelPricingEditor.jsxweb/classic/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.jsweb/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/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.json
✅ Files skipped from review due to trivial changes (7)
- web/default/src/features/channels/lib/channel-utils.ts
- web/default/src/i18n/locales/zh.json
- web/default/src/i18n/locales/ru.json
- web/default/src/i18n/locales/ja.json
- relay/channel/task/taskcommon/usd_billing_test.go
- web/default/src/i18n/locales/fr.json
- web/default/src/i18n/locales/en.json
| resp, err := adaptor.FetchTask(baseURL, key, relaycommon.BuildTaskFetchBody( | ||
| task.GetUpstreamTaskID(), | ||
| task.Action, | ||
| task.Properties.UpstreamModelName, | ||
| ), proxy) |
There was a problem hiding this comment.
Add a backward-compatible fallback for UpstreamModelName.
This polling path now depends on task.Properties.UpstreamModelName. If that field is empty for older queued tasks, or for any submit path that never persisted it, the post-deploy fetch payload becomes incomplete and those video jobs can stop updating. Please fall back to the saved request model, or omit the model field when it is blank.
🤖 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/task_video.go` around lines 83 - 87, The call to adaptor.FetchTask
currently always passes task.Properties.UpstreamModelName into
BuildTaskFetchBody which can be empty for older tasks; update the code around
the adaptor.FetchTask / BuildTaskFetchBody invocation to use a
backward-compatible fallback: if task.Properties.UpstreamModelName is empty,
pass the saved request model (e.g., task.RequestModel or the persisted
request-model field on the task) instead, and if neither exists pass an
empty/omitted model value so BuildTaskFetchBody generates a payload without the
model field. Ensure the conditional checks reference
task.Properties.UpstreamModelName and the saved request model symbol you have
(e.g., task.RequestModel) so the fetch payload is complete for older queued
jobs.
| billingSetting.UpstreamCostMultiplier["test-model"] = 7.3 | ||
| if got := ResolveUpstreamCostMultiplier("test-model"); got != 7.3 { | ||
| t.Fatalf("got %v want 7.3", got) | ||
| } | ||
| if got := ResolveUpstreamCostMultiplier("missing"); got != 1 { | ||
| t.Fatalf("missing should default to 1, got %v", got) | ||
| } | ||
| delete(billingSetting.UpstreamCostMultiplier, "test-model") | ||
| } |
There was a problem hiding this comment.
Use t.Cleanup to guarantee test-state rollback.
The test mutates global state, but cleanup at Line 14 is skipped if an earlier assertion fails. Register cleanup immediately to keep tests isolated.
Suggested fix
func TestResolveUpstreamCostMultiplier(t *testing.T) {
ensureBillingSettingMaps()
billingSetting.UpstreamCostMultiplier["test-model"] = 7.3
+ t.Cleanup(func() {
+ delete(billingSetting.UpstreamCostMultiplier, "test-model")
+ })
if got := ResolveUpstreamCostMultiplier("test-model"); got != 7.3 {
t.Fatalf("got %v want 7.3", got)
}
if got := ResolveUpstreamCostMultiplier("missing"); got != 1 {
t.Fatalf("missing should default to 1, got %v", got)
}
- delete(billingSetting.UpstreamCostMultiplier, "test-model")
}🤖 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/billing_setting/upstream_cost_multiplier_test.go` around lines 7 -
15, The test mutates global map billingSetting.UpstreamCostMultiplier and
currently calls delete(...) at the end which will be skipped on failure; fix by
registering a t.Cleanup callback immediately after setting
billingSetting.UpstreamCostMultiplier["test-model"] = 7.3 (use t.Cleanup to
delete that key) so the map entry is removed regardless of test failures, then
proceed to call ResolveUpstreamCostMultiplier and the assertions as before.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Capture confirmed design for submit + polling failover across video channels with ordered list override and billing rules. Co-authored-by: Cursor <cursoragent@cursor.com>
Break the confirmed design into sequenced tasks covering classification, settings, submit snapshot, orchestrator, cross-channel recreate, and default UI. Co-authored-by: Cursor <cursoragent@cursor.com>
Add TaskPrivateData failover fields and shared audit vs upstream-balance classification in taskcommon; mao same-channel path uses the shared helper. Co-authored-by: Cursor <cursoragent@cursor.com>
Register TaskSameChannelMaxRetries, TaskCrossChannelFailoverEnabled, and TaskModelChannelOrder with getters for ordered channel overrides. Co-authored-by: Cursor <cursoragent@cursor.com>
Add ListSatisfiedChannelIDs and ResolveTaskFailoverChannelIDs so optional model channel order can override Priority snapshots. Co-authored-by: Cursor <cursoragent@cursor.com>
Persist client/upstream bodies and candidate channel IDs on create; non-auto groups retry along ordered failover list when configured. Co-authored-by: Cursor <cursoragent@cursor.com>
Route polling FAILURE through HandleAsyncTaskFailure; same-channel uses TaskAsyncFailureResubmitter; cross-channel hooks TaskFailoverRecreateFunc. Co-authored-by: Cursor <cursoragent@cursor.com>
Add SkipPreConsume recreate path from client_request_body and wire TaskFailoverRecreateFunc in main. Co-authored-by: Cursor <cursoragent@cursor.com>
Use PrivateData/SameChannelMaxRetries for same-channel caps and progress labels; keep balance blocking same-channel only. Co-authored-by: Cursor <cursoragent@cursor.com>
Add same-channel retry / cross-channel failover controls and JSON editor for TaskModelChannelOrder under operations settings. Co-authored-by: Cursor <cursoragent@cursor.com>
Add same-channel / cross-channel toggles in general settings and a drag-and-drop model channel order editor under operation settings. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores