feat(doubao): 接入 Seedance 2.0 素材资产管理 9 个接口#5656
Conversation
Add VolcAssetSettings (AK/SK, region, project, group) and wire VolcAssetConfig into the option map for read/update.
Add signed (volc AK/SK) request handlers for the Doubao Asset API: list/get/create/update/delete assets and create/list/get/update asset groups.
Register POST /doubao/open/* routes (token-authenticated) and the controller handlers proxying to the Doubao Asset client.
WalkthroughAdds a Volcengine Doubao Asset API proxy. A new ChangesDoubao Asset API Proxy
Sequence Diagram(s)sequenceDiagram
participant Client
participant DoubaoRouter
participant Controller
participant Handler as doubao.Handle*
participant signRequest
participant VolcengineArk as Volcengine Ark API
Client->>DoubaoRouter: POST /doubao/assets/list (TokenAuth)
DoubaoRouter->>Controller: RelayListAssets(c)
Controller->>Handler: HandleListAssets(c)
Handler->>Handler: unmarshal body, apply VolcAssetConfig defaults
Handler->>signRequest: build + sign POST request (HMAC-SHA256)
signRequest-->>Handler: Authorization header set
Handler->>VolcengineArk: POST ?Action=ListAssets&Version=2024-01-01
VolcengineArk-->>Handler: JSON envelope (Result or Error)
Handler-->>Client: JSON result or structured error
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
relay/channel/task/doubao/asset.go (4)
444-494: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winValidate pagination parameters.
HandleListAssetGroupsdoes not validate thatPageNumberandPageSizeare provided or within acceptable ranges, similar to the issue inHandleListAssets.✅ Suggested validation
func HandleListAssetGroups(c *gin.Context) { var req ListAssetGroupsRequest if err := common.UnmarshalBodyReusable(c, &req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid request body: %v", err)}) return } + if req.PageNumber <= 0 || req.PageSize <= 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "PageNumber and PageSize must be greater than 0"}) + return + } if req.Filter == nil {🤖 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/asset.go` around lines 444 - 494, Add validation for pagination parameters in the HandleListAssetGroups function. After unmarshaling the request body, validate that PageNumber and PageSize are provided and within acceptable ranges before proceeding with the API call. If either parameter is missing or out of range, return a bad request response with an appropriate error message. This validation should be similar to what is implemented in HandleListAssets and should occur before the applyDefaults call.
417-438: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winValidate required field.
HandleCreateAssetGroupdoes not validate the requiredNamefield. Validating early provides clearer error messages to the client.✅ Suggested validation
func HandleCreateAssetGroup(c *gin.Context) { var req CreateAssetGroupRequest if err := common.UnmarshalBodyReusable(c, &req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid request body: %v", err)}) return } + if req.Name == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Name is required"}) + return + } applyDefaults(&req.ProjectName, nil, &req.GroupType)🤖 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/asset.go` around lines 417 - 438, The HandleCreateAssetGroup function does not validate that the required Name field is present in the CreateAssetGroupRequest struct. Add a validation check immediately after calling common.UnmarshalBodyReusable to ensure that req.Name is not empty, and if it is empty, return a clear error response to the client with an appropriate HTTP status code (such as BadRequest) before proceeding with the applyDefaults call and further processing.
330-352: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winValidate required fields.
HandleCreateAssetdoes not validate required fields such asURLandAssetType. If the client omits these, the upstream API will reject the request, but validating early provides clearer error messages.✅ Suggested validation
func HandleCreateAsset(c *gin.Context) { var req CreateAssetRequest if err := common.UnmarshalBodyReusable(c, &req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid request body: %v", err)}) return } + if req.URL == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "URL is required"}) + return + } + if req.AssetType == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "AssetType is required"}) + return + } applyDefaults(&req.ProjectName, &req.GroupId, nil)🤖 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/asset.go` around lines 330 - 352, The HandleCreateAsset function does not validate required fields before passing the request to the upstream API. Add validation logic after unmarshaling the request body to check that the URL and AssetType fields in CreateAssetRequest are not empty strings. If either field is missing, return a http.StatusBadRequest response with a clear error message indicating which required field is missing, similar to the existing error handling pattern in the function. This validation should occur before calling applyDefaults to ensure invalid requests fail fast with user-friendly error messages.
276-297: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winValidate pagination parameters.
HandleListAssetsdoes not validate thatPageNumberandPageSizeare provided or within acceptable ranges. If the client omits these fields, they default to0, which may not be valid for the upstream Volcengine API and could result in confusing error messages.✅ Suggested validation
func HandleListAssets(c *gin.Context) { var listReq ListAssetsRequest if err := common.UnmarshalBodyReusable(c, &listReq); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid request body: %v", err)}) return } + if listReq.PageNumber <= 0 || listReq.PageSize <= 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "PageNumber and PageSize must be greater than 0"}) + return + } if listReq.Filter == nil {🤖 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/asset.go` around lines 276 - 297, The HandleListAssets function needs to validate pagination parameters to prevent invalid values from being sent to the upstream Volcengine API. After unmarshaling the request body (after the UnmarshalBodyReusable call but before the Marshal call), add validation to check that PageNumber and PageSize have valid, non-zero values. If either parameter is missing or out of acceptable range, return a BadRequest response with a clear error message indicating what values are required, similar to how the invalid request body error is already handled.
🤖 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/asset.go`:
- Around line 30-69: The PageNumber and PageSize fields in the ListAssetsRequest
struct are defined as non-pointer int64 types without omitempty tags, which
prevents distinguishing between absent values (that should be nil) and explicit
zero values. Change these two fields from int64 to *int64 and add omitempty to
their JSON tags, so they become json:"PageNumber,omitempty" and
json:"PageSize,omitempty" respectively, to align with the coding guidelines for
optional scalar fields in relay request DTOs like the other optional fields
(Filter, SortBy, SortOrder, ProjectName) already defined in the struct.
---
Nitpick comments:
In `@relay/channel/task/doubao/asset.go`:
- Around line 444-494: Add validation for pagination parameters in the
HandleListAssetGroups function. After unmarshaling the request body, validate
that PageNumber and PageSize are provided and within acceptable ranges before
proceeding with the API call. If either parameter is missing or out of range,
return a bad request response with an appropriate error message. This validation
should be similar to what is implemented in HandleListAssets and should occur
before the applyDefaults call.
- Around line 417-438: The HandleCreateAssetGroup function does not validate
that the required Name field is present in the CreateAssetGroupRequest struct.
Add a validation check immediately after calling common.UnmarshalBodyReusable to
ensure that req.Name is not empty, and if it is empty, return a clear error
response to the client with an appropriate HTTP status code (such as BadRequest)
before proceeding with the applyDefaults call and further processing.
- Around line 330-352: The HandleCreateAsset function does not validate required
fields before passing the request to the upstream API. Add validation logic
after unmarshaling the request body to check that the URL and AssetType fields
in CreateAssetRequest are not empty strings. If either field is missing, return
a http.StatusBadRequest response with a clear error message indicating which
required field is missing, similar to the existing error handling pattern in the
function. This validation should occur before calling applyDefaults to ensure
invalid requests fail fast with user-friendly error messages.
- Around line 276-297: The HandleListAssets function needs to validate
pagination parameters to prevent invalid values from being sent to the upstream
Volcengine API. After unmarshaling the request body (after the
UnmarshalBodyReusable call but before the Marshal call), add validation to check
that PageNumber and PageSize have valid, non-zero values. If either parameter is
missing or out of acceptable range, return a BadRequest response with a clear
error message indicating what values are required, similar to how the invalid
request body error is already handled.
🪄 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: c0835726-ae3c-4a6e-a55e-8b5c8005bdfd
📒 Files selected for processing (5)
controller/asset.gomodel/option.gorelay/channel/task/doubao/asset.gorouter/video-router.gosetting/system_setting/volc_asset.go
| type AssetFilter struct { | ||
| GroupIds []string `json:"GroupIds,omitempty"` | ||
| GroupType string `json:"GroupType" example:"AIGC"` // 可选,留空使用默认值(AIGC) | ||
| Statuses []string `json:"Statuses,omitempty"` | ||
| Name string `json:"Name,omitempty"` | ||
| } | ||
|
|
||
| type ListAssetsRequest struct { | ||
| Filter *AssetFilter `json:"Filter,omitempty"` | ||
| PageNumber int64 `json:"PageNumber"` | ||
| PageSize int64 `json:"PageSize"` | ||
| SortBy string `json:"SortBy,omitempty"` | ||
| SortOrder string `json:"SortOrder,omitempty"` | ||
| ProjectName string `json:"ProjectName,omitempty"` // 可选,留空使用默认值 | ||
| } | ||
|
|
||
| type AssetError struct { | ||
| Code string `json:"Code,omitempty"` | ||
| Message string `json:"Message,omitempty"` | ||
| } | ||
|
|
||
| type AssetItem struct { | ||
| Id string `json:"Id"` | ||
| Name string `json:"Name"` | ||
| URL string `json:"URL"` | ||
| GroupId string `json:"GroupId"` | ||
| AssetType string `json:"AssetType" example:"Image" enums:"Image,Video,Audio"` // 素材类型:Image=图像, Video=视频, Audio=音频 | ||
| Status string `json:"Status"` | ||
| Error AssetError `json:"Error,omitempty"` | ||
| ProjectName string `json:"ProjectName"` | ||
| CreateTime string `json:"CreateTime"` | ||
| UpdateTime string `json:"UpdateTime"` | ||
| } | ||
|
|
||
| type ListAssetsResponse struct { | ||
| Items []AssetItem `json:"Items"` | ||
| TotalCount int64 `json:"TotalCount"` | ||
| PageNumber int64 `json:"PageNumber"` | ||
| PageSize int64 `json:"PageSize"` | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if there are validation patterns for pagination in similar handlers
rg -nP -A5 'PageNumber|PageSize' --type=go -g 'relay/**/*.go' -g '!asset.go'Repository: QuantumNous/new-api
Length of output: 45
🌐 Web query:
Volcengine Asset API ListAssets PageNumber PageSize parameter requirements
💡 Result:
I couldn’t find (from the retrieved official sources) an authoritative Volcengine “Asset API / ListAssets” reference page that explicitly states the pagination parameter requirements for PageNumber and PageSize (e.g., min/max/default, whether page number is 1-based or 0-based). What I found instead: - There is a Volcengine “火山方舟-查询素材资产列表(ListAssets)” documentation page, but the content I retrieved does not include the specific PageNumber/PageSize constraint details needed to answer your question [1]. - A third-party mirror (Anyfast) documents a /volc/asset/ListAssets endpoint, but it also doesn’t include the PageNumber/PageSize constraint requirements in the retrieved material [2][3]. - Another Volcengine API (RDS for MySQL “ListResourcePackages”) shows pagination constraints (min page number 1; page size max 1000), but it is for a different API and I can’t apply those limits to ListAssets without confirmation [4]. To give you a precise, citation-backed answer, I need one of the following: 1) the exact Volcengine product/service name for “Asset API ListAssets” (e.g., 火山方舟/Seedance 2.0 素材库, 资产管理, 容器镜像仓库等), or 2) the specific API reference URL (or screenshot/text) where PageNumber and PageSize are defined. Once you share that, I can do a targeted re-search and extract the exact parameter rules for PageNumber/PageSize.
Citations:
- 1: https://www.volcengine.com/docs/82379/2318273?lang=zh
- 2: https://docs.anyfast.ai/api-reference/model-api/bytedance/volc-asset-list-assets
- 3: https://docs.anyfast.ai/zh/api-reference/model-api/bytedance/volc-asset-list-assets
- 4: https://www.volcengine.com/docs/6313/1804897?lang=en
🏁 Script executed:
# Search for ListAssetsRequest usage and validation
find relay/channel/task/doubao -type f -name "*.go" | head -20Repository: QuantumNous/new-api
Length of output: 174
🏁 Script executed:
# Check if there's validation logic for pagination parameters
rg -A10 "ListAssetsRequest" relay/channel/task/doubao/ --type=goRepository: QuantumNous/new-api
Length of output: 1668
🏁 Script executed:
# Look for how other relay channels handle pagination parameters
rg -B2 -A2 "PageNumber.*int|PageSize.*int" relay/ --type=goRepository: QuantumNous/new-api
Length of output: 1868
🏁 Script executed:
# Check if there are similar API request structs to see patterns
rg "PageNumber|PageSize" relay/channel --type=go -B1 -A1Repository: QuantumNous/new-api
Length of output: 1319
🏁 Script executed:
# Read the full handler function to see complete context around ListAssetsRequest usage
rg -B5 -A30 "listReq :=" relay/channel/task/doubao/asset.go --type=goRepository: QuantumNous/new-api
Length of output: 45
🏁 Script executed:
# Check if there's any validation of PageNumber/PageSize values downstream
rg -B2 -A5 "PageNumber|PageSize" relay/channel/task/doubao/adaptor.go --type=goRepository: QuantumNous/new-api
Length of output: 45
🏁 Script executed:
# Look for the actual upstream API call to see if defaults are applied
rg -B5 -A10 "ListAssets\|listReq" relay/channel/task/doubao/adaptor.go --type=goRepository: QuantumNous/new-api
Length of output: 45
🏁 Script executed:
# Read the entire asset.go file to understand the full context
wc -l relay/channel/task/doubao/asset.goRepository: QuantumNous/new-api
Length of output: 102
🏁 Script executed:
# Read asset.go in full or sections to understand ListAssetsRequest usage
cat -n relay/channel/task/doubao/asset.go | head -150Repository: QuantumNous/new-api
Length of output: 5535
🏁 Script executed:
# Also check adaptor.go to see how requests are forwarded
cat -n relay/channel/task/doubao/adaptor.goRepository: QuantumNous/new-api
Length of output: 13601
🏁 Script executed:
# Search for the handler function that parses ListAssetsRequest
rg -B2 -A50 "ListAssetsRequest" relay/channel/task/doubao/asset.go | head -100Repository: QuantumNous/new-api
Length of output: 3522
🏁 Script executed:
# Check if there's validation of PageNumber or PageSize anywhere
rg "PageNumber|PageSize" relay/channel/task/doubao/asset.go -B2 -A2Repository: QuantumNous/new-api
Length of output: 1028
Use pointer types with omitempty for optional pagination parameters.
PageNumber and PageSize in ListAssetsRequest (lines 39-40) and ListAssetGroupsRequest are non-pointer int64 fields without omitempty tags. Per coding guidelines, optional scalar fields in relay request DTOs must use pointer types with omitempty to distinguish between absent (nil) and explicit zero values.
The handler HandleListAssets does not validate these parameters or apply defaults. If the client omits these fields, they will unmarshal to 0 and be sent to the upstream Volcengine API without validation, potentially causing unexpected behavior if 0 is not a valid value.
Change these fields to *int64 with json:"PageNumber,omitempty" and json:"PageSize,omitempty" to preserve the distinction between absent and explicit zero values, consistent with how other optional fields (Filter, SortBy, SortOrder, ProjectName) are defined.
🤖 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/asset.go` around lines 30 - 69, The PageNumber and
PageSize fields in the ListAssetsRequest struct are defined as non-pointer int64
types without omitempty tags, which prevents distinguishing between absent
values (that should be nil) and explicit zero values. Change these two fields
from int64 to *int64 and add omitempty to their JSON tags, so they become
json:"PageNumber,omitempty" and json:"PageSize,omitempty" respectively, to align
with the coding guidelines for optional scalar fields in relay request DTOs like
the other optional fields (Filter, SortBy, SortOrder, ProjectName) already
defined in the struct.
Source: Coding guidelines
接入火山引擎 Seedance 2.0 官方格式的 9 个素材(Asset)管理接口,以代理转发方式统一收口到网关:请求侧用平台 Token 鉴权,网关内部用火山 AK/SK 对上游签名。
官方文档: https://www.volcengine.com/docs/82379/2333589?lang=zh
/doubao/open/CreateAsset/doubao/open/CreateAssetGroup/doubao/open/DeleteAsset/doubao/open/GetAsset/doubao/open/GetAssetGroup/doubao/open/ListAssetGroups/doubao/open/ListAssets/doubao/open/UpdateAsset/doubao/open/UpdateAssetGroup📝 变更描述 / Description
为豆包(火山引擎)新增一套素材资产管理的代理接口,客户端无需自行持有火山 AK/SK,即可通过网关完成素材与素材组的增删改查。实现分三层:
setting/system_setting/volc_asset.go,定义VolcAssetSettings(AK/SK、region、project、默认 group 等);通过model/option.go把VolcAssetConfig接入 options 配置体系,支持热更新。Region 缺省cn-beijing,GroupType 缺省AIGC。relay/channel/task/doubao/asset.go,实现对火山 Asset API(service=ark,version=2024-01-01)的请求构造与 AK/SK 签名,覆盖 list/get/create/update/delete asset 与 create/list/get/update asset group。controller/asset.go提供 9 个 handler,router/video-router.go注册/doubao/open/*路由组,统一走TokenAuth鉴权。生效原理:网关接收客户端的平台 Token 请求 → 取出已配置的火山 AK/SK 对上游做签名 → 透传转发到火山 Asset API 并回传结果,从而屏蔽上游鉴权细节、统一入口与权限控制。
配置说明: 启用前需在 options 表写入一条
key = VolcAssetConfig的配置(代码已在InitOptionMap自动注册并支持热更新,无需改表结构),value为如下 JSON:{ "access_key": "AKLT****your-volc-ak****", "secret_key": "****your-volc-sk****", "region": "cn-beijing", "project_name": "default", "group_id": "", "group_type": "AIGC" }其中
access_key/secret_key必填,其余可选(region缺省cn-beijing,group_type缺省AIGC)。🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
go build ./...通过,并验证路由与接口可用。📸 运行证明 / Proof of Work
(在此粘贴 9 个接口的调用截图 / 关键日志 / 测试报告)
Summary by CodeRabbit