Skip to content

Cscitech#7

Merged
CSCITech merged 3 commits into
mainfrom
cscitech
Jul 4, 2026
Merged

Cscitech#7
CSCITech merged 3 commits into
mainfrom
cscitech

Conversation

@CSCITech

@CSCITech CSCITech commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

修复视频任务参数化计费的两个边界问题。

本次调整让 multipart 视频任务在经过 Param Override 改写后,也会把最终上游 JSON 请求体同步到计费上下文,避免分辨率、时长、音频或模型等字段按改写前请求计费。同时修正通用任务计费中的图片数量统计,避免顶层 image / images 已从最终请求体计数后,又从同步后的 TaskSubmitReq 镜像字段重复累加。

这些改动保证预扣费依据与实际上游请求保持一致,也让使用 image_count 作为匹配条件或计量字段的 rate card 不会被重复计数放大费用。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • Closes # (无)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

本地验证通过:

go test ./relay/channel/task/taskcommon -run "TestEstimateGenericTaskBilling" -count=1
ok  	github.com/MAX-API-Next/MAX-API/relay/channel/task/taskcommon

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7213ab7e-d027-4a94-9380-8f71d0a60998

📥 Commits

Reviewing files that changed from the base of the PR and between 2253cc5 and f21a8dc.

📒 Files selected for processing (6)
  • relay/channel/task/doubao/adaptor.go
  • relay/channel/task/doubao/adaptor_billing_test.go
  • relay/channel/task/taskcommon/generic_billing.go
  • relay/channel/task/taskcommon/path_config_test.go
  • relay/channel/task/taskcommon/request_body.go
  • relay/common/relay_info.go
📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Backend checks
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrappers in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly calling encoding/json; encoding/json types like json.RawMessage and json.Number may still be used as types.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+ simultaneously: prefer GORM methods over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use cross-DB quoting/boolean helpers when raw SQL is unavoidable, avoid DB-specific functions/operators without fallbacks, and ensure migrations work on all three databases.
For request structs parsed from client JSON and later re-marshaled to upstream providers, especially in relay/convert paths, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved and omitted-only fields remain nil.

**/*.go: Use common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, and common.GetJsonType from common/json.go for all JSON marshal/unmarshal operations; do not directly call encoding/json in business code.
Keep all database code fully compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless cross-DB-safe, and use the provided DB-specific helpers/flags for quoting, booleans, and branching.
For request structs parsed from client JSON and re-marshaled to upstream providers, especially in relay/convert paths, use pointer types with omitempty for optional scalar fields so explicit zero/false values are preserved.

Files:

  • relay/channel/task/taskcommon/path_config_test.go
  • relay/channel/task/taskcommon/request_body.go
  • relay/channel/task/doubao/adaptor_billing_test.go
  • relay/channel/task/doubao/adaptor.go
  • relay/channel/task/taskcommon/generic_billing.go
  • relay/common/relay_info.go
relay/channel/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.

When implementing a new channel, confirm whether the provider supports StreamOptions; if it does, add the channel to streamSupportedChannels.

Files:

  • relay/channel/task/taskcommon/path_config_test.go
  • relay/channel/task/taskcommon/request_body.go
  • relay/channel/task/doubao/adaptor_billing_test.go
  • relay/channel/task/doubao/adaptor.go
  • relay/channel/task/taskcommon/generic_billing.go
🔇 Additional comments (10)
relay/channel/task/doubao/adaptor.go (1)

48-50: LGTM!

Also applies to: 450-522, 552-559

relay/channel/task/doubao/adaptor_billing_test.go (1)

228-294: LGTM!

Also applies to: 326-411, 413-448, 480-507

relay/common/relay_info.go (1)

699-722: LGTM!

relay/channel/task/taskcommon/request_body.go (2)

74-89: LGTM!


110-137: LGTM!

Also applies to: 214-222

relay/channel/task/taskcommon/path_config_test.go (1)

110-137: LGTM!

relay/channel/task/taskcommon/generic_billing.go (4)

14-38: LGTM!


78-109: stringPtrValue correctly guards the new pointer Ratio field.

Good defensive handling to avoid a nil dereference now that req.Ratio is *string.


46-52: 📐 Maintainability & Code Quality

No change needed.

			> Likely an incorrect or invalid review comment.

214-262: 🎯 Functional Correctness

Drop this concern
genericBillingMediaCount only falls back to req.Images/req.Image when the raw key is absent, and the focused test still totals 3 for 2 images + 1 reference image with no extra accumulation.

			> Likely an incorrect or invalid review comment.

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Expanded task request normalization to support additional top-level options (callbacks, timing/execution, ratio/content, audio generation, tools, safety/priority/frames/seed, camera and watermark).
    • Added generic task billing estimation with richer request parsing and rate-card matching.
  • Bug Fixes

    • Improved validation to allow official content-only submissions without a top-level prompt.
    • Fixed audio request mapping and improved media detection/counting for more accurate billing.
  • Tests

    • Added/updated coverage for request normalization, billing estimation, media handling, multipart overrides, and validation behavior.

Walkthrough

This PR adds a generic task-billing estimator, expands task submit requests with top-level Seedance fields, updates request-body syncing and relay billing wiring, and extends the Doubao adaptor to validate and map the new fields.

Changes

Task billing and Seedance field support

Layer / File(s) Summary
TaskSubmitReq schema expansion
relay/common/relay_info.go
Adds JSON fields for ratio, content, callback URL, return frame control, service tier, execution expiry, audio, tools, safety, priority, frames, seed, camera, and watermark.
Request body merge and sync updates
relay/channel/task/taskcommon/request_body.go, relay/channel/task/taskcommon/path_config_test.go
Stores the task submit body after validation, maps the new top-level fields into TaskSubmitReq, adds slice and pointer helpers, and adds a sync test for official Seedance fields.
Generic task billing estimation engine
relay/channel/task/taskcommon/generic_billing.go, relay/channel/task/taskcommon/generic_billing_test.go
Adds EstimateGenericTaskBilling plus helpers for model, duration, ratio, audio, video, and media-count derivation, with tests for rate-card selection, request-body precedence, video gating, and image counting.
Relay submit billing wiring
relay/relay_task.go, relay/relay_task_test.go
Routes billing through estimateTaskBilling, falls back to the generic estimator, applies returned quota overrides, and adds tests for multipart overrides and generic fallback billing.
Doubao request validation and top-level content
relay/channel/task/doubao/adaptor.go, relay/channel/task/doubao/adaptor_billing_test.go
Branches validation by content type, accepts usable official content without a top-level prompt, and adds validation coverage for that path.
Doubao payload conversion for top-level Seedance options
relay/channel/task/doubao/adaptor.go, relay/channel/task/doubao/adaptor_billing_test.go
Maps top-level Seedance fields into the upstream payload, adjusts prompt handling, and updates tests for payload mapping and optional empty fields.
Doubao billing coverage
relay/channel/task/doubao/adaptor_billing_test.go
Adds a Doubao-specific rate-card billing test against the generic estimator.
Estimated code review effort: 4 (Complex) ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RelayTaskSubmit
  participant taskcommon.EstimateGenericTaskBilling
  participant task_billing_setting

  Client->>RelayTaskSubmit: submit task request
  RelayTaskSubmit->>taskcommon.EstimateGenericTaskBilling: estimate billing
  taskcommon.EstimateGenericTaskBilling->>task_billing_setting: match rate card and calculate
  task_billing_setting-->>taskcommon.EstimateGenericTaskBilling: billing result
  taskcommon.EstimateGenericTaskBilling-->>RelayTaskSubmit: quota and billing fields
  RelayTaskSubmit-->>Client: continue task submission
Loading

Poem

A rabbit hops through billing trails,
With Seedance fields and prompty rails 🐇
Quotas hop, and rate cards sing,
Content, media, and counts take wing,
Hop hoooray—this PR now sails!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is too vague to convey the main change and does not help teammates identify the PR. Replace it with a concise summary like "Sync param-overridden task requests into billing context and fix image counting".
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description matches the implemented fixes for param override billing sync and generic image-count deduplication.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cscitech

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
relay/channel/task/taskcommon/request_body.go (1)

73-89: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Body is stored before JSON unmarshal is confirmed to succeed.

StoreTaskSubmitRequestBody (line 77) runs right after the loose common.IsJsonObject check but before common.Unmarshal (lines 79-81) actually validates the payload. If IsJsonObject only checks brace framing rather than full JSON validity, a structurally-broken body can still be stored in the context before the function returns an error. Given the current caller (relay_task.go) aborts the whole request on error, this has no observable effect today, but it's fragile: any future caller that tolerates the error (e.g. retries reusing the same gin.Context) would carry over a stale/invalid stored body into billing (EstimateGenericTaskBilling silently swallows unmarshal errors on that stored body). Consider moving the store call after the Unmarshal succeeds.

♻️ Suggested reordering
 func SyncTaskRequestContext(c *gin.Context, data []byte) error {
 	if c == nil || c.Request == nil || len(bytes.TrimSpace(data)) == 0 || !common.IsJsonObject(string(data)) {
 		return nil
 	}
-	relaycommon.StoreTaskSubmitRequestBody(c, data)
 	var raw map[string]any
 	if err := common.Unmarshal(data, &raw); err != nil {
 		return err
 	}
+	relaycommon.StoreTaskSubmitRequestBody(c, data)
 	req := relaycommon.TaskSubmitReq{}
🤖 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/taskcommon/request_body.go` around lines 73 - 89, In
SyncTaskRequestContext, the task body is being stored in the gin.Context before
common.Unmarshal has validated that the payload is actually valid JSON, which
can leave stale or invalid data behind. Move the
relaycommon.StoreTaskSubmitRequestBody call to after the common.Unmarshal(data,
&raw) check succeeds, and keep the mergeTaskSubmitReq / c.Set("task_request",
req) flow unchanged so only confirmed-valid bodies are persisted.
relay/channel/task/doubao/adaptor.go (1)

415-460: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve image items when top-level content is present

convertToRequestPayload appends req.Images first, then applyTopLevelSeedanceOptions replaces r.Content whenever req.Content is non-empty. TaskSubmitReq allows images and content to coexist, so this drops user-provided image inputs. Merge the top-level content into r.Content instead of overwriting 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 `@relay/channel/task/doubao/adaptor.go` around lines 415 - 460,
`convertToRequestPayload` is losing image inputs because
`applyTopLevelSeedanceOptions` replaces `r.Content` when `req.Content` is
present. Update `applyTopLevelSeedanceOptions` (and its `topLevelContentItems`
flow) so it merges the parsed top-level content into the existing
`requestPayload.Content` instead of overwriting it, preserving any `image_url`
items added from `req.Images`. Ensure the final `r.Content` keeps both images
and text items when `TaskSubmitReq` provides both.
🤖 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/task/doubao/adaptor.go`:
- Around line 549-556: The helper firstNonEmptyString in adaptor.go checks
emptiness with strings.TrimSpace but returns the original untrimmed value, so
callers like the r.Ratio assignment can receive padded input. Update
firstNonEmptyString to return the trimmed string for the first non-empty value,
keeping the existing empty check behavior but ensuring consistent normalized
output.

In `@relay/channel/task/taskcommon/path_config_test.go`:
- Around line 110-145: The test duplicates the same JSON payload in both the
task context and the SyncTaskRequestContext input, but the context body is not
used by SyncTaskRequestContext. Simplify
TestSyncTaskRequestContextReadsOfficialSeedanceFields by defining the JSON once
and reusing it for newJSONTaskContext and SyncTaskRequestContext, or by removing
the unused context payload entirely; keep the assertions on
relaycommon.GetTaskRequest and the request fields unchanged.

In `@relay/channel/task/taskcommon/request_body.go`:
- Around line 126-138: The request body parsing in the task common request
handling has a redundant duplicate mapping for generate_audio into both
req.WithAudio and req.GenerateAudio. Update the parsing logic in the request
body decoder to treat req.GenerateAudio as the canonical target and remove the
extra generate_audio assignment to req.WithAudio, keeping only the field used by
the Doubao adaptor’s fallback path.

In `@relay/common/relay_info.go`:
- Around line 699-722: The request struct in relay_info.go has several newly
added optional scalar fields defined as plain strings, which loses the
distinction between an omitted value and an explicitly provided empty string.
Change Ratio, CallbackURL, ServiceTier, and SafetyIdentifier in the relevant
request type to pointer strings with omitempty, then update the related
merge/copy path (including mergeTaskSubmitReq and any copyString helper usage)
and the Doubao adaptor checks so nil vs empty is handled correctly when
forwarding options like applyTopLevelSeedanceOptions.

---

Outside diff comments:
In `@relay/channel/task/doubao/adaptor.go`:
- Around line 415-460: `convertToRequestPayload` is losing image inputs because
`applyTopLevelSeedanceOptions` replaces `r.Content` when `req.Content` is
present. Update `applyTopLevelSeedanceOptions` (and its `topLevelContentItems`
flow) so it merges the parsed top-level content into the existing
`requestPayload.Content` instead of overwriting it, preserving any `image_url`
items added from `req.Images`. Ensure the final `r.Content` keeps both images
and text items when `TaskSubmitReq` provides both.

In `@relay/channel/task/taskcommon/request_body.go`:
- Around line 73-89: In SyncTaskRequestContext, the task body is being stored in
the gin.Context before common.Unmarshal has validated that the payload is
actually valid JSON, which can leave stale or invalid data behind. Move the
relaycommon.StoreTaskSubmitRequestBody call to after the common.Unmarshal(data,
&raw) check succeeds, and keep the mergeTaskSubmitReq / c.Set("task_request",
req) flow unchanged so only confirmed-valid bodies are persisted.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 0fa69fa0-4b3f-4532-b4a2-5309de3eeca5

📥 Commits

Reviewing files that changed from the base of the PR and between b10421a and 2253cc5.

📒 Files selected for processing (9)
  • relay/channel/task/doubao/adaptor.go
  • relay/channel/task/doubao/adaptor_billing_test.go
  • relay/channel/task/taskcommon/generic_billing.go
  • relay/channel/task/taskcommon/generic_billing_test.go
  • relay/channel/task/taskcommon/path_config_test.go
  • relay/channel/task/taskcommon/request_body.go
  • relay/common/relay_info.go
  • relay/relay_task.go
  • relay/relay_task_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrappers in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly calling encoding/json; encoding/json types like json.RawMessage and json.Number may still be used as types.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+ simultaneously: prefer GORM methods over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use cross-DB quoting/boolean helpers when raw SQL is unavoidable, avoid DB-specific functions/operators without fallbacks, and ensure migrations work on all three databases.
For request structs parsed from client JSON and later re-marshaled to upstream providers, especially in relay/convert paths, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved and omitted-only fields remain nil.

**/*.go: Use common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, and common.GetJsonType from common/json.go for all JSON marshal/unmarshal operations; do not directly call encoding/json in business code.
Keep all database code fully compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless cross-DB-safe, and use the provided DB-specific helpers/flags for quoting, booleans, and branching.
For request structs parsed from client JSON and re-marshaled to upstream providers, especially in relay/convert paths, use pointer types with omitempty for optional scalar fields so explicit zero/false values are preserved.

Files:

  • relay/channel/task/taskcommon/path_config_test.go
  • relay/common/relay_info.go
  • relay/relay_task.go
  • relay/channel/task/taskcommon/generic_billing_test.go
  • relay/channel/task/taskcommon/request_body.go
  • relay/relay_task_test.go
  • relay/channel/task/doubao/adaptor_billing_test.go
  • relay/channel/task/taskcommon/generic_billing.go
  • relay/channel/task/doubao/adaptor.go
relay/channel/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.

When implementing a new channel, confirm whether the provider supports StreamOptions; if it does, add the channel to streamSupportedChannels.

Files:

  • relay/channel/task/taskcommon/path_config_test.go
  • relay/channel/task/taskcommon/generic_billing_test.go
  • relay/channel/task/taskcommon/request_body.go
  • relay/channel/task/doubao/adaptor_billing_test.go
  • relay/channel/task/taskcommon/generic_billing.go
  • relay/channel/task/doubao/adaptor.go
🔇 Additional comments (15)
relay/channel/task/taskcommon/generic_billing.go (2)

14-60: LGTM!

Also applies to: 70-436


61-69: 🎯 Functional Correctness

UpstreamModelName is promoted from *ChannelMeta The guard is fine here: RelayInfo embeds *ChannelMeta, and UpstreamModelName lives on ChannelMeta, so this reads the intended field.

			> Likely an incorrect or invalid review comment.
relay/channel/task/taskcommon/generic_billing_test.go (1)

20-284: LGTM!

relay/relay_task.go (1)

201-208: LGTM!

Also applies to: 282-320

relay/relay_task_test.go (1)

10-46: LGTM!

Also applies to: 93-190

relay/common/relay_info.go (1)

733-778: LGTM!

relay/channel/task/taskcommon/request_body.go (2)

110-125: LGTM!

Also applies to: 129-176


298-313: 🎯 Functional Correctness

No change needed for zero values copyIntPtr and copyBoolPtr already preserve 0 and false by decoding the raw map value directly, so these fields remain non-nil when present.

			> Likely an incorrect or invalid review comment.
relay/channel/task/taskcommon/path_config_test.go (1)

136-144: LGTM!

relay/channel/task/doubao/adaptor.go (2)

145-158: 🎯 Functional Correctness | ⚡ Quick win

Verify omission of reference_images normalization in the JSON branch.

The pass-through branch (Lines 134-136) maps req.ReferenceImages into req.Images when images is empty, but this standard JSON branch only normalizes req.Image (Lines 155-157). If a JSON request supplies only reference_images, those inputs won't be surfaced via req.Images. Confirm this divergence is intentional.


226-252: LGTM!

relay/channel/task/doubao/adaptor_billing_test.go (4)

40-47: LGTM!


228-294: LGTM!


326-409: LGTM!


466-493: LGTM!

Comment thread relay/channel/task/doubao/adaptor.go
Comment thread relay/channel/task/taskcommon/path_config_test.go
Comment thread relay/channel/task/taskcommon/request_body.go
Comment thread relay/common/relay_info.go Outdated
@CSCITech
CSCITech merged commit fd18116 into main Jul 4, 2026
3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jul 5, 2026
11 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant