Skip to content

feat: 视频生成接口新增 Seedance(火山 ARK)原生请求/响应兼容入口 - #5737

Open
ChenYichener wants to merge 1 commit into
QuantumNous:mainfrom
ChenYichener:feat/seedance-native-compat
Open

feat: 视频生成接口新增 Seedance(火山 ARK)原生请求/响应兼容入口#5737
ChenYichener wants to merge 1 commit into
QuantumNous:mainfrom
ChenYichener:feat/seedance-native-compat

Conversation

@ChenYichener

@ChenYichener ChenYichener commented Jun 25, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 本 PR 代码为 AI 辅助(AI-assisted) 完成:实现移植自作者已自测通过的分支,并由作者人工审阅、本地编译与测试验证。以下描述由作者基于对代码逻辑的理解整理撰写。

📝 变更描述 / Description

为视频生成接口新增一套 Seedance(火山引擎 ARK)原生请求/响应兼容入口,路径前缀为 /seedance/api/v3/contents/generations,与官方 ARK 视频接口对齐。

设计动机:项目此前已通过 relay/channel/task/doubao 适配器支持 Seedance 模型,但缺少面向客户端的原生入口;原本直接调用 Seedance/火山 ARK 视频 SDK 的用户无法像可灵(/kling)、即梦(/jimeng)用户那样仅靠切换 base_url 就接入。本 PR 补齐这一兼容层,请求与响应两端都保持 Seedance 原生形态,做到零代码改动、仅替换 base_url 即可迁移。

实现要点(沿用现有 Kling/Jimeng 原生路由模式):

  • router (router/video-router.go):注册原生任务的提交、按 ID 查询、列表查询路由。
  • middleware (middleware/seedance_adapter.go):SeedanceRequestConvert() 标记官方 API 上下文并将 POST 路径改写到内部统一的 /v1/video/generations
  • controller (controller/seedance.go):RelaySeedanceTask / RelaySeedanceTaskFetch 处理器。
  • relay (relay/seedance_task.go):按 ID / 列表查询任务,并以上游原生响应结构回包(含分页与 filter.* 过滤)。
  • doubao adaptor (relay/channel/task/doubao/adaptor.go):新增官方 API 分支,透传原生 content 负载、回包上游 task_id,并补充 expired/cancelled 状态映射。
  • common (common/gin.go):新增 KeySeedanceOfficialAPI 上下文键。

遵循项目规范:JSON 统一走 common.*;DB 查询使用 GORM 方法、兼容 SQLite/MySQL/PostgreSQL;未引入数据库特定特性。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix)
  • ✨ 新功能 (New feature)
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 Issues 与 PRs,确认不是重复提交。
  • Bug fix 说明: 不适用(本 PR 为新功能)。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地编译相关包并通过新增单元测试(中间件 / doubao 适配器 / relay 响应组装共 7 个测试全部通过)。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

本地验证(Go 1.26.4):

$ go test ./middleware/ -run Seedance -v
--- PASS: TestSeedanceRequestConvertSubmit
--- PASS: TestSeedanceRequestConvertFetchByID
--- PASS: TestSeedanceRequestConvertFetchList
ok  github.com/QuantumNous/new-api/middleware

$ go test ./relay/channel/task/doubao/ -run Seedance -v
--- PASS: TestSeedanceOfficialBuildRequestBodyPreservesNativeContent
--- PASS: TestSeedanceOfficialDoResponseReturnsUpstreamTaskID
ok  github.com/QuantumNous/new-api/relay/channel/task/doubao

$ go test ./relay/ -run Seedance -v
--- PASS: TestSeedanceTaskIDFilters
--- PASS: TestSeedanceTaskResponseUsesUpstreamShape
ok  github.com/QuantumNous/new-api/relay

go vet 改动涉及包无新增问题;改动包均编译通过。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for Seedance official API requests and task relay endpoints.
    • Introduced task list and single-task retrieval responses with filtering, pagination, and consistent result formatting.
    • Added route handling for Seedance generation and task endpoints.
  • Bug Fixes

    • Improved handling of failed and expired tasks with clearer error reporting.
    • Ensured request conversion preserves key submission details and returns the expected task ID for official API flows.
  • Tests

    • Added coverage for request conversion, task response shaping, and task ID filtering.

Add a dedicated /seedance/api/v3/contents/generations prefix so clients
using the Seedance / VolcEngine ARK video SDK can switch base_url to
new-api without changing request/response shapes, mirroring the existing
Kling and Jimeng native routes.

- router: register native submit/fetch/list task routes
- middleware: SeedanceRequestConvert marks official API and rewrites path
- controller: RelaySeedanceTask / RelaySeedanceTaskFetch handlers
- relay: seedance_task fetch-by-id and list with upstream-shaped response
- doubao adaptor: official-API branch preserving native content payload
- tests for middleware, doubao adaptor and relay response shaping

Closes QuantumNous#5735

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds a Seedance official API route group, request-conversion middleware, controller handlers, and relay logic for task submission and retrieval. The Doubao adaptor now branches on the official API context, and tests cover middleware, adaptor, and task helpers.

Changes

Seedance official API compatibility

Layer / File(s) Summary
Ingress wiring and request flagging
common/gin.go, middleware/seedance_adapter.go, controller/seedance.go, router/video-router.go, middleware/seedance_adapter_test.go
Adds the Seedance official API key, request conversion middleware, controller handlers, route registration, and middleware tests for task submit and fetch endpoints.
Doubao official API branch
relay/channel/task/doubao/adaptor.go, relay/channel/task/doubao/adaptor_test.go
Adds Seedance-specific request parsing, request-body rebuilds, upstream ID responses, and expired/cancelled status mapping in the Doubao adaptor.
Seedance task retrieval and shaping
relay/seedance_task.go, relay/relay_task_seedance_test.go
Implements Seedance single-task and list retrieval with task-ID filters, pagination, upstream-ID fallback, and response shaping.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SeedanceRequestConvert
  participant RelaySeedanceTask
  participant RelayTask
  participant RelaySeedanceTaskFetch
  participant SeedanceTaskFetch
  participant TaskAdaptor

  Client->>SeedanceRequestConvert: POST /seedance/api/v3/contents/generations/tasks
  SeedanceRequestConvert->>RelaySeedanceTask: c.Next() after setting KeySeedanceOfficialAPI, rewriting POST path, and setting relay_mode
  RelaySeedanceTask->>RelayTask: RelayTask(c)
  RelayTask->>TaskAdaptor: BuildRequestBody / DoResponse

  Client->>SeedanceRequestConvert: GET /seedance/api/v3/contents/generations/tasks or /tasks/:task_id
  SeedanceRequestConvert->>RelaySeedanceTaskFetch: c.Next() after setting KeySeedanceOfficialAPI
  RelaySeedanceTaskFetch->>SeedanceTaskFetch: fetch task or list payload
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • QuantumNous/new-api#1553: Adjusts the same /v1/video/generations relay path that the new Seedance middleware rewrites POST submissions toward.
  • QuantumNous/new-api#4042: Overlaps with the Seedance-specific request and response shaping added in relay/channel/task/doubao/adaptor.go.
  • QuantumNous/new-api#4180: Also changes the Doubao adaptor’s native request/response handling for Seedance-style traffic.

Suggested reviewers

  • seefs001 — touches Seedance routing, middleware, and relay adaptation.

Poem

A bunny did hop through Seedance lanes,
With JSON crumbs and gleaming chains.
New routes bloomed bright, task IDs did sing,
And the carrot-cart returned one tidy thing.
🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and clearly describes the main change: adding Seedance native request/response compatibility.
Linked Issues check ✅ Passed The PR implements the requested Seedance-native routing, request conversion middleware, relay shaping, and doubao adapter branch.
Out of Scope Changes check ✅ Passed The changes stay focused on Seedance compatibility plumbing, routing, relay behavior, and tests without unrelated scope creep.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
relay/seedance_task.go (1)

44-106: 🚀 Performance & Scalability | 🔵 Trivial

Unbounded in-memory load before pagination.

seedanceFetchTaskList fetches every matching task for the user (7-day window, no LIMIT) and then filters and paginates entirely in memory. The same pattern appears in seedanceGetTaskByID (Lines 114-128), which loads all candidate tasks to match an upstream ID. For users with high task volume this scales poorly in memory and DB transfer, and frequent polling amplifies it.

If pure status/model/service_tier filtering dominates, consider pushing those into the query (and an index on (user_id, platform, submit_time)), keeping only the filter.* JSON-derived predicates in memory.

🤖 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/seedance_task.go` around lines 44 - 106, The task list retrieval in
seedanceFetchTaskList is loading all matching tasks into memory before applying
pagination, causing unbounded DB transfer and memory use. Push the simple
filters that can be expressed in SQL (user_id, platform, submit_time, and any
direct status/model/service_tier conditions) into the GORM query, then apply
offset/limit there instead of slicing the full result set in Go. Keep only the
JSON-derived predicates from seedanceTaskIDFilters and any helper-based matching
in memory, and apply the same approach to seedanceGetTaskByID where it scans
candidates.
🤖 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 204-227: In BuildRequestBody’s Seedance official API branch,
info.UpstreamModelName is only used when already populated, so it can stay empty
even though the request body contains the model. Before rewriting bodyMap,
explicitly read bodyMap["model"] and assign it to info.UpstreamModelName when it
is currently unset, then continue with the existing override/Marshal flow so
billing attribution always has the upstream model name.

---

Nitpick comments:
In `@relay/seedance_task.go`:
- Around line 44-106: The task list retrieval in seedanceFetchTaskList is
loading all matching tasks into memory before applying pagination, causing
unbounded DB transfer and memory use. Push the simple filters that can be
expressed in SQL (user_id, platform, submit_time, and any direct
status/model/service_tier conditions) into the GORM query, then apply
offset/limit there instead of slicing the full result set in Go. Keep only the
JSON-derived predicates from seedanceTaskIDFilters and any helper-based matching
in memory, and apply the same approach to seedanceGetTaskByID where it scans
candidates.
🪄 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: 3d30af99-be54-49c5-9478-7a7021582d66

📥 Commits

Reviewing files that changed from the base of the PR and between b191f47 and 0e0be0f.

📒 Files selected for processing (9)
  • common/gin.go
  • controller/seedance.go
  • middleware/seedance_adapter.go
  • middleware/seedance_adapter_test.go
  • relay/channel/task/doubao/adaptor.go
  • relay/channel/task/doubao/adaptor_test.go
  • relay/relay_task_seedance_test.go
  • relay/seedance_task.go
  • router/video-router.go

Comment on lines +204 to +227
if c.GetBool(common.KeySeedanceOfficialAPI) {
storage, err := common.GetBodyStorage(c)
if err != nil {
return nil, err
}
cachedBody, err := storage.Bytes()
if err != nil {
return nil, err
}

var bodyMap map[string]interface{}
if err := common.Unmarshal(cachedBody, &bodyMap); err != nil {
return bytes.NewReader(cachedBody), nil
}
if info.UpstreamModelName != "" {
bodyMap["model"] = info.UpstreamModelName
}
data, err := common.Marshal(bodyMap)
if err != nil {
return nil, err
}
return bytes.NewReader(data), nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find where UpstreamModelName / OriginModelName are populated in the task relay flow
rg -nP '\b(UpstreamModelName|OriginModelName)\s*=' --type=go -g '!**/*_test.go' relay/
# Inspect how TaskSubmitReq.Model feeds into RelayInfo
rg -nP -C3 '\bTaskSubmitReq\b' --type=go relay/common/

Repository: QuantumNous/new-api

Length of output: 7991


🏁 Script executed:

sed -n '119,145p' relay/channel/task/doubao/adaptor.go

Repository: QuantumNous/new-api

Length of output: 1116


🏁 Script executed:

sed -n '204,230p' relay/channel/task/doubao/adaptor.go

Repository: QuantumNous/new-api

Length of output: 799


Ensure info.UpstreamModelName is populated for Seedance billing attribution.

In the Seedance branch (BuildRequestBody), info.UpstreamModelName is only used to override the request model if it has already been set (e.g., by model mapping). However, if no mapping occurred, info.UpstreamModelName remains empty despite the model being present in the request body.

While the code correctly omits the override in this case, relying solely on implicit behavior risks missing the upstream model name for billing and usage tracking if it hasn't been populated elsewhere. It is safer to explicitly extract and assign the model from the body to info if it is currently unset.

Consider adding the following logic before modifying bodyMap:

if info.UpstreamModelName == "" {
    if m, ok := bodyMap["model"].(string); ok && m != "" {
        info.UpstreamModelName = m
    }
}
🤖 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 204 - 227, In
BuildRequestBody’s Seedance official API branch, info.UpstreamModelName is only
used when already populated, so it can stay empty even though the request body
contains the model. Before rewriting bodyMap, explicitly read bodyMap["model"]
and assign it to info.UpstreamModelName when it is currently unset, then
continue with the existing override/Marshal flow so billing attribution always
has the upstream model name.

@chucklvip

Copy link
Copy Markdown

@QuentinHsu
强烈建议合并此PR,Sora停服,Seedance 独领风骚

@wangzhongwei

Copy link
Copy Markdown

官方赶紧合并啊

@CherryRum

Copy link
Copy Markdown

这个没有大佬审核下吗

JacksonsY pushed a commit to JacksonsY/new-api that referenced this pull request Jul 15, 2026
从上游 QuantumNous/new-api PR QuantumNous#5737 (@ChenYichener) 应用。为 seedance 新增官方火山 ARK
原生入口 /seedance/api/v3/contents/generations(对齐 kling/jimeng 原生路由模式),原本直连
火山 ARK SDK 的用户仅换 base_url 即可接入。

- router:注册提交/按 ID 查询/列表查询路由(TokenAuth + Distribute)。
- middleware SeedanceRequestConvert:标记官方 API 上下文并把 POST 改写到 /v1/video/generations。
- controller/relay:seedance_task.go 按 id/列表查询(含分页与 filter.*,按 user_id 隔离)。
- doubao 适配器:新增官方 API 分支(校验 model/content、透传 content、回包 task_id),
  并补 expired/cancelled 终态映射。
- Closes upstream QuantumNous#5735。

Review:构建/测试通过;JSON 走 common.*、GORM 跨库、查询按 user_id 隔离、路由带鉴权;
计费复用 doubao 现有链路(未新增计费代码)。小提示:列表查询在内存里过滤+分页(7 天窗口内有界)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JacksonsY added a commit to JacksonsY/new-api that referenced this pull request Jul 15, 2026
PR QuantumNous#5737 的列表查询原本把用户近 7 天全部任务读进内存再过滤+分页;重度视频用户 7 天
可能累积上千条,每次列表请求都全量载入并反序列化 Data JSON,浪费明显。

- 无过滤条件时走快路径:DB 级 COUNT + LIMIT/OFFSET,不再全量载入。
- model/service_tier 存于 task.Data JSON、跨库无法可靠下推,故带这些(或 status/
  task_ids)过滤时仍回退内存路径,保证过滤+分页语义正确。
- 抽出 seedanceTaskBaseQuery(Count/Find 各取独立查询)与 seedanceMarshalTaskList,
  顺带 dedup seedanceGetTaskByID 的相同基础查询。
- 新增 TestSeedanceFetchTaskListPagination:用内存 SQLite 同时护住两条路径的
  用户/时间窗/平台过滤、id 倒序、分页与 total。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JacksonsY added a commit to JacksonsY/new-api that referenced this pull request Jul 15, 2026
AIAI 视频渠道适配器 + Seedance 火山原生入口(上游 PR QuantumNous#5737)+ 任务计费倍率日志展示
+ seedance 列表 DB 级分页。与 main 的 supplier/detector 修复文件集不重叠,无冲突。
@chucklvip

Copy link
Copy Markdown

@Calcium-Ion

请关注下这个PR

@jwister

jwister commented Jul 22, 2026

Copy link
Copy Markdown

合并进去了吗?

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.

Seedance(火山引擎 ARK)原生请求/响应兼容入口

5 participants