新增支持 Sora-2-Pro 1080P,支持 豆包Seedance 2.0 视频 官方原生路由 - #4180
Conversation
WalkthroughThis PR adds support for Doubao's native video generation API integration. It introduces request middleware to translate native API calls into the internal relay format, a response converter interface to map internal task records back to native format, new route handlers for Doubao endpoints, and updates Sora billing/validation logic. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant DoubaoMiddleware as DoubaoRequestConvert<br/>Middleware
participant RelayTask as Relay Task<br/>Pipeline
participant TaskAdaptor as Doubao Task<br/>Adaptor
participant ResponseConverter as Response<br/>Converter
rect rgba(100, 150, 200, 0.5)
Note over Client,ResponseConverter: Doubao Native Request Flow
Client->>DoubaoMiddleware: POST /api/v3/contents/generations/tasks<br/>(native request body)
DoubaoMiddleware->>DoubaoMiddleware: Parse native request<br/>Extract model, prompts, images
DoubaoMiddleware->>DoubaoMiddleware: Build unified internal payload<br/>with metadata normalization
DoubaoMiddleware->>RelayTask: Forward to /v1/video/generations<br/>(internal format body)
RelayTask->>RelayTask: Process relay task<br/>(platform routing, execution)
RelayTask->>TaskAdaptor: Create/track task
end
rect rgba(150, 100, 200, 0.5)
Note over Client,ResponseConverter: Doubao Native Response Flow
Client->>RelayTask: GET /api/v3/contents/generations/tasks/:task_id
RelayTask->>RelayTask: Detect doubao_native_route flag
RelayTask->>TaskAdaptor: Retrieve internal task record
RelayTask->>ResponseConverter: ConvertToDoubaoNativeResponse(task)
ResponseConverter->>ResponseConverter: Map internal status<br/>to Doubao format
ResponseConverter->>ResponseConverter: Build response with<br/>video_url/error/usage
ResponseConverter-->>RelayTask: Doubao native JSON
RelayTask-->>Client: Doubao native response
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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.
🧹 Nitpick comments (5)
middleware/doubao_adapter.go (2)
209-212: Missingdoubao_native_routeflag for POST requests.The GET handler sets
c.Set("doubao_native_route", true)(Line 73), but the POST handler does not. While the submit response is handled differently (viaDoResponsein the adaptor), setting this flag consistently could be useful for future logging, metrics, or response customization.♻️ Optional: Set doubao_native_route for POST consistency
// Redirect to the internal video generation endpoint c.Request.URL.Path = "/v1/video/generations" + c.Set("doubao_native_route", true) c.Next()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@middleware/doubao_adapter.go` around lines 209 - 212, The POST branch that rewrites requests to c.Request.URL.Path = "/v1/video/generations" is missing the same c.Set("doubao_native_route", true) that the GET handler sets; add c.Set("doubao_native_route", true) in the POST handler (before calling c.Next() / before the path rewrite or immediately after) so the POST path follows the same flag convention used by the GET route and downstream logic like DoResponse in the adaptor can rely on it for logging/metrics/customization.
85-101: Empty prompt validation missing.If the native request contains no
"text"type content items,promptwill be an empty string. The downstreamValidateBasicTaskRequest(viavalidatePrompt) will catch this, but returning an error earlier with a Doubao-specific message would provide better UX.♻️ Optional: Add early prompt validation
prompt := strings.Join(promptParts, "\n") + + if strings.TrimSpace(prompt) == "" { + abortWithOpenAiMessage(c, http.StatusBadRequest, "content must include at least one text item with non-empty text") + return + } // Build metadata — carry all non-standard fields so the doubao adaptor🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@middleware/doubao_adapter.go` around lines 85 - 101, The code builds prompt from nativeReq.Content into promptParts/images but does not validate an empty prompt early; after the existing loop that constructs prompt (using promptParts, images and prompt) add an early check if prompt == "" and return a Doubao-specific error response (instead of deferring to ValidateBasicTaskRequest/validatePrompt) so callers get immediate, user-friendly feedback; locate the validation right after prompt := strings.Join(promptParts, "\n") in middleware/doubao_adapter.go and return the appropriate error payload expected by the surrounding handler.relay/relay_task.go (2)
402-411: Fallback path silently swallows conversion errors.When
DoubaoNativeResponseConverteris not implemented and theOpenAIVideoConverterfallback fails, the error is silently discarded (Line 406 only returns onconvertErr == nil). This allows execution to continue to the generic TaskDto path, which may not be the intended behavior for Doubao native routes.Consider either logging the error or returning it explicitly to avoid masking conversion failures.
♻️ Suggested fix to handle fallback error
// Fallback: if the platform doesn't implement DoubaoNativeResponseConverter, // return OpenAI Video API format as the next best option. if converter, ok := adaptor.(channel.OpenAIVideoConverter); ok { openAIVideoData, convertErr := converter.ConvertToOpenAIVideo(originTask) - if convertErr == nil { - respBody = openAIVideoData - return + if convertErr != nil { + common.SysError("doubao_native_route fallback to OpenAI format failed: " + convertErr.Error()) } + respBody = openAIVideoData + return }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@relay/relay_task.go` around lines 402 - 411, The fallback branch that attempts to use channel.OpenAIVideoConverter currently swallows conversion errors (ConvertToOpenAIVideo on originTask) and only proceeds on success; update that branch in relay_task.go so conversion failures are surfaced instead of ignored: capture convertErr and either log it with the component logger (including converter type and originTask ID/context) or return it up the call chain (so respBody is not silently replaced by the generic TaskDto path); modify the code around the adaptor.(channel.OpenAIVideoConverter) check and the handling of respBody/return to propagate the conversion error for Doubao native routes.
390-412: Consider handlingniladaptor explicitly fordoubao_native_route.If
GetTaskAdaptor(originTask.Platform)returnsnil(Line 391), the code falls through to theisOpenAIVideoAPIbranch or generic TaskDto path. Fordoubao_native_route, this could return an unexpected response format to the downstream client expecting Doubao-native JSON.♻️ Optional: Return explicit error for unsupported platform
if c.GetBool("doubao_native_route") { adaptor := GetTaskAdaptor(originTask.Platform) - if adaptor != nil { + if adaptor == nil { + taskResp = service.TaskErrorWrapperLocal( + fmt.Errorf("unsupported platform for doubao native route: %s", originTask.Platform), + "unsupported_platform", http.StatusNotImplemented) + return + } + { if converter, ok := adaptor.(channel.DoubaoNativeResponseConverter); ok {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@relay/relay_task.go` around lines 390 - 412, When doubao_native_route is enabled and GetTaskAdaptor(originTask.Platform) returns nil, explicitly return a clear error instead of falling through; in the doubao_native_route branch (around GetTaskAdaptor and the checks for channel.DoubaoNativeResponseConverter and channel.OpenAIVideoConverter) detect adaptor == nil and call service.TaskErrorWrapper with an appropriate error message and code (e.g., "unsupported_platform_for_doubao_native" and http.StatusBadRequest or http.StatusInternalServerError) so the caller receives an explicit Doubao-native format error rather than an unexpected OpenAI/generic TaskDto response.relay/channel/task/doubao/adaptor.go (1)
408-413: Error details may be empty when task data is unavailable.When
doubaoStatus == "failed"butoriginTask.Datais empty or fails to unmarshal (Line 379 silently ignores errors),dResp.Error.CodeanddResp.Error.Messagewill be empty strings. Consider falling back tooriginTask.FailReasonfor the message:♻️ Suggested fix to use FailReason as fallback
if doubaoStatus == "failed" { + errMsg := dResp.Error.Message + if errMsg == "" { + errMsg = originTask.FailReason + } native["error"] = map[string]interface{}{ "code": dResp.Error.Code, - "message": dResp.Error.Message, + "message": errMsg, } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@relay/channel/task/doubao/adaptor.go` around lines 408 - 413, When doubaoStatus == "failed" the code currently uses dResp.Error.Code and dResp.Error.Message which can be empty if originTask.Data was missing or unmarshal silently failed; update the failure branch that builds native["error"] to use a fallback: set the "message" to dResp.Error.Message if non-empty, otherwise use originTask.FailReason (and similarly fall back for "code" if appropriate), ensuring you check originTask.FailReason for emptiness before assigning; modify the error-building logic in the same function where doubaoStatus, dResp, originTask, and native are referenced so the native["error"] always contains a meaningful message when available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@middleware/doubao_adapter.go`:
- Around line 209-212: The POST branch that rewrites requests to
c.Request.URL.Path = "/v1/video/generations" is missing the same
c.Set("doubao_native_route", true) that the GET handler sets; add
c.Set("doubao_native_route", true) in the POST handler (before calling c.Next()
/ before the path rewrite or immediately after) so the POST path follows the
same flag convention used by the GET route and downstream logic like DoResponse
in the adaptor can rely on it for logging/metrics/customization.
- Around line 85-101: The code builds prompt from nativeReq.Content into
promptParts/images but does not validate an empty prompt early; after the
existing loop that constructs prompt (using promptParts, images and prompt) add
an early check if prompt == "" and return a Doubao-specific error response
(instead of deferring to ValidateBasicTaskRequest/validatePrompt) so callers get
immediate, user-friendly feedback; locate the validation right after prompt :=
strings.Join(promptParts, "\n") in middleware/doubao_adapter.go and return the
appropriate error payload expected by the surrounding handler.
In `@relay/channel/task/doubao/adaptor.go`:
- Around line 408-413: When doubaoStatus == "failed" the code currently uses
dResp.Error.Code and dResp.Error.Message which can be empty if originTask.Data
was missing or unmarshal silently failed; update the failure branch that builds
native["error"] to use a fallback: set the "message" to dResp.Error.Message if
non-empty, otherwise use originTask.FailReason (and similarly fall back for
"code" if appropriate), ensuring you check originTask.FailReason for emptiness
before assigning; modify the error-building logic in the same function where
doubaoStatus, dResp, originTask, and native are referenced so the
native["error"] always contains a meaningful message when available.
In `@relay/relay_task.go`:
- Around line 402-411: The fallback branch that attempts to use
channel.OpenAIVideoConverter currently swallows conversion errors
(ConvertToOpenAIVideo on originTask) and only proceeds on success; update that
branch in relay_task.go so conversion failures are surfaced instead of ignored:
capture convertErr and either log it with the component logger (including
converter type and originTask ID/context) or return it up the call chain (so
respBody is not silently replaced by the generic TaskDto path); modify the code
around the adaptor.(channel.OpenAIVideoConverter) check and the handling of
respBody/return to propagate the conversion error for Doubao native routes.
- Around line 390-412: When doubao_native_route is enabled and
GetTaskAdaptor(originTask.Platform) returns nil, explicitly return a clear error
instead of falling through; in the doubao_native_route branch (around
GetTaskAdaptor and the checks for channel.DoubaoNativeResponseConverter and
channel.OpenAIVideoConverter) detect adaptor == nil and call
service.TaskErrorWrapper with an appropriate error message and code (e.g.,
"unsupported_platform_for_doubao_native" and http.StatusBadRequest or
http.StatusInternalServerError) so the caller receives an explicit Doubao-native
format error rather than an unexpected OpenAI/generic TaskDto response.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 756296b9-49e8-45a8-bb82-2fa2aa978b23
📒 Files selected for processing (7)
middleware/doubao_adapter.gorelay/channel/adapter.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/sora/adaptor.gorelay/common/relay_utils.gorelay/relay_task.gorouter/video-router.go
Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)
Summary by CodeRabbit
New Features
Improvements