-
Notifications
You must be signed in to change notification settings - Fork 11.6k
新增: 豆包视频1.5pro #2632
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
新增: 豆包视频1.5pro #2632
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package dto | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "strconv" | ||
| ) | ||
|
|
||
| type IntValue int | ||
|
|
||
| func (i *IntValue) UnmarshalJSON(b []byte) error { | ||
| var n int | ||
| if err := json.Unmarshal(b, &n); err == nil { | ||
| *i = IntValue(n) | ||
| return nil | ||
| } | ||
| var s string | ||
| if err := json.Unmarshal(b, &s); err != nil { | ||
| return err | ||
| } | ||
| v, err := strconv.Atoi(s) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| *i = IntValue(v) | ||
| return nil | ||
| } | ||
|
|
||
| func (i IntValue) MarshalJSON() ([]byte, error) { | ||
| return json.Marshal(int(i)) | ||
| } | ||
|
|
||
| type BoolValue bool | ||
|
|
||
| func (b *BoolValue) UnmarshalJSON(data []byte) error { | ||
| var boolean bool | ||
| if err := json.Unmarshal(data, &boolean); err == nil { | ||
| *b = BoolValue(boolean) | ||
| return nil | ||
| } | ||
| var str string | ||
| if err := json.Unmarshal(data, &str); err != nil { | ||
| return err | ||
| } | ||
| if str == "true" { | ||
| *b = BoolValue(true) | ||
| } else if str == "false" { | ||
| *b = BoolValue(false) | ||
| } else { | ||
| return json.Unmarshal(data, &boolean) | ||
| } | ||
| return nil | ||
| } | ||
| func (b BoolValue) MarshalJSON() ([]byte, error) { | ||
| return json.Marshal(bool(b)) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,9 @@ import ( | |
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| "time" | ||
|
|
||
| "github.com/QuantumNous/new-api/common" | ||
|
|
||
| "github.com/QuantumNous/new-api/constant" | ||
| "github.com/QuantumNous/new-api/dto" | ||
|
|
@@ -23,18 +26,36 @@ import ( | |
| // ============================ | ||
|
|
||
| type ContentItem struct { | ||
| Type string `json:"type"` // "text" or "image_url" | ||
| Text string `json:"text,omitempty"` // for text type | ||
| ImageURL *ImageURL `json:"image_url,omitempty"` // for image_url type | ||
| Type string `json:"type"` // "text", "image_url" or "video" | ||
| Text string `json:"text,omitempty"` // for text type | ||
| ImageURL *ImageURL `json:"image_url,omitempty"` // for image_url type | ||
| Video *VideoReference `json:"video,omitempty"` // for video (sample) type | ||
| } | ||
|
|
||
| type ImageURL struct { | ||
| URL string `json:"url"` | ||
| } | ||
|
|
||
| type VideoReference struct { | ||
| URL string `json:"url"` // Draft video URL | ||
| } | ||
|
|
||
| type requestPayload struct { | ||
| Model string `json:"model"` | ||
| Content []ContentItem `json:"content"` | ||
| Model string `json:"model"` | ||
| Content []ContentItem `json:"content"` | ||
| CallbackURL string `json:"callback_url,omitempty"` | ||
| ReturnLastFrame *dto.BoolValue `json:"return_last_frame,omitempty"` | ||
| ServiceTier string `json:"service_tier,omitempty"` | ||
| ExecutionExpiresAfter dto.IntValue `json:"execution_expires_after,omitempty"` | ||
| GenerateAudio *dto.BoolValue `json:"generate_audio,omitempty"` | ||
| Draft *dto.BoolValue `json:"draft,omitempty"` | ||
| Resolution string `json:"resolution,omitempty"` | ||
| Ratio string `json:"ratio,omitempty"` | ||
| Duration dto.IntValue `json:"duration,omitempty"` | ||
| Frames dto.IntValue `json:"frames,omitempty"` | ||
| Seed dto.IntValue `json:"seed,omitempty"` | ||
| CameraFixed *dto.BoolValue `json:"camera_fixed,omitempty"` | ||
| Watermark *dto.BoolValue `json:"watermark,omitempty"` | ||
| } | ||
|
|
||
| type responsePayload struct { | ||
|
|
@@ -53,6 +74,7 @@ type responseTask struct { | |
| Duration int `json:"duration"` | ||
| Ratio string `json:"ratio"` | ||
| FramesPerSecond int `json:"framespersecond"` | ||
| ServiceTier string `json:"service_tier"` | ||
| Usage struct { | ||
| CompletionTokens int `json:"completion_tokens"` | ||
| TotalTokens int `json:"total_tokens"` | ||
|
|
@@ -98,16 +120,16 @@ func (a *TaskAdaptor) BuildRequestHeader(c *gin.Context, req *http.Request, info | |
|
|
||
| // BuildRequestBody converts request into Doubao specific format. | ||
| func (a *TaskAdaptor) BuildRequestBody(c *gin.Context, info *relaycommon.RelayInfo) (io.Reader, error) { | ||
| v, exists := c.Get("task_request") | ||
| if !exists { | ||
| return nil, fmt.Errorf("request not found in context") | ||
| req, err := relaycommon.GetTaskRequest(c) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| req := v.(relaycommon.TaskSubmitReq) | ||
|
|
||
| body, err := a.convertToRequestPayload(&req) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "convert request payload failed") | ||
| } | ||
| info.UpstreamModelName = body.Model | ||
| data, err := json.Marshal(body) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -141,7 +163,13 @@ func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *rela | |
| return | ||
| } | ||
|
|
||
| c.JSON(http.StatusOK, gin.H{"task_id": dResp.ID}) | ||
| ov := dto.NewOpenAIVideo() | ||
| ov.ID = dResp.ID | ||
| ov.TaskID = dResp.ID | ||
| ov.CreatedAt = time.Now().Unix() | ||
| ov.Model = info.OriginModelName | ||
|
|
||
| c.JSON(http.StatusOK, ov) | ||
| return dResp.ID, responseBody, nil | ||
| } | ||
|
|
||
|
|
@@ -204,12 +232,15 @@ func (a *TaskAdaptor) convertToRequestPayload(req *relaycommon.TaskSubmitReq) (* | |
| } | ||
| } | ||
|
|
||
| // TODO: Add support for additional parameters from metadata | ||
| // such as ratio, duration, seed, etc. | ||
| // metadata := req.Metadata | ||
| // if metadata != nil { | ||
| // // Parse and apply metadata parameters | ||
| // } | ||
| metadata := req.Metadata | ||
| medaBytes, err := json.Marshal(metadata) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "metadata marshal metadata failed") | ||
| } | ||
| err = json.Unmarshal(medaBytes, &r) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "unmarshal metadata failed") | ||
| } | ||
|
|
||
| return &r, nil | ||
| } | ||
|
|
@@ -229,7 +260,7 @@ func (a *TaskAdaptor) ParseTaskResult(respBody []byte) (*relaycommon.TaskInfo, e | |
| case "pending", "queued": | ||
| taskResult.Status = model.TaskStatusQueued | ||
| taskResult.Progress = "10%" | ||
| case "processing": | ||
| case "processing", "running": | ||
| taskResult.Status = model.TaskStatusInProgress | ||
| taskResult.Progress = "50%" | ||
| case "succeeded": | ||
|
|
@@ -251,3 +282,30 @@ func (a *TaskAdaptor) ParseTaskResult(respBody []byte) (*relaycommon.TaskInfo, e | |
|
|
||
| return &taskResult, nil | ||
| } | ||
|
|
||
| func (a *TaskAdaptor) ConvertToOpenAIVideo(originTask *model.Task) ([]byte, error) { | ||
| var dResp responseTask | ||
| if err := json.Unmarshal(originTask.Data, &dResp); err != nil { | ||
| return nil, errors.Wrap(err, "unmarshal doubao task data failed") | ||
| } | ||
|
|
||
| openAIVideo := dto.NewOpenAIVideo() | ||
| openAIVideo.ID = originTask.TaskID | ||
| openAIVideo.TaskID = originTask.TaskID | ||
| openAIVideo.Status = originTask.Status.ToVideoStatus() | ||
| openAIVideo.SetProgressStr(originTask.Progress) | ||
| openAIVideo.SetMetadata("url", dResp.Content.VideoURL) | ||
| openAIVideo.CreatedAt = originTask.CreatedAt | ||
| openAIVideo.CompletedAt = originTask.UpdatedAt | ||
| openAIVideo.Model = originTask.Properties.OriginModelName | ||
|
|
||
| if dResp.Status == "failed" { | ||
| openAIVideo.Error = &dto.OpenAIVideoError{ | ||
| Message: "task failed", | ||
| Code: "failed", | ||
| } | ||
| } | ||
|
|
||
| jsonData, _ := common.Marshal(openAIVideo) | ||
| return jsonData, nil | ||
| } | ||
|
Comment on lines
+286
to
+311
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cd /tmp && find . -type f -name "*.go" | grep -E "(adaptor|task_video)" | head -20Repository: QuantumNous/new-api Length of output: 45 🏁 Script executed: # First, let's locate the file and examine the actual code
fd -t f "adaptor.go" | grep doubaoRepository: QuantumNous/new-api Length of output: 99 🏁 Script executed: # Get the file structure and check the doubao adaptor
git ls-files | grep -E "(relay/channel|controller)" | head -30Repository: QuantumNous/new-api Length of output: 774 🏁 Script executed: # Search for the doubao adaptor file
find . -name "adaptor.go" -path "*/doubao/*" 2>/dev/nullRepository: QuantumNous/new-api Length of output: 101 🏁 Script executed: # Read the doubao adaptor file to examine the ConvertToOpenAIVideo function
cat -n relay/channel/task/doubao/adaptor.go | head -320Repository: QuantumNous/new-api Length of output: 11781 🏁 Script executed: # Search for task_video.go to understand the sanitization patterns
find . -name "task_video.go" 2>/dev/nullRepository: QuantumNous/new-api Length of output: 89 🏁 Script executed: # Check the model definitions for TaskStatus
rg "TaskStatus" --type go -A 3 | head -50Repository: QuantumNous/new-api Length of output: 2328 🏁 Script executed: # Examine the task_video.go to understand sanitization patterns
cat -n controller/task_video.go | head -350Repository: QuantumNous/new-api Length of output: 12733 🏁 Script executed: # Look for the redactVideoResponseBody function
rg "redactVideoResponseBody|bytesBase64Encoded" --type go -B 5 -A 10Repository: QuantumNous/new-api Length of output: 6983 🏁 Script executed: # Check model/task.go to verify TaskStatus constants
cat -n model/task.go | grep -A 20 "type TaskStatus"Repository: QuantumNous/new-api Length of output: 711 🏁 Script executed: # Check for other similar adaptor implementations to understand patterns
find relay/channel/task -name "adaptor.go" -type f | head -5Repository: QuantumNous/new-api Length of output: 244 🏁 Script executed: # Look at another adaptor to see how they handle ConvertToOpenAIVideo
rg "ConvertToOpenAIVideo" --type go -A 20 | head -80Repository: QuantumNous/new-api Length of output: 5038 🏁 Script executed: # Check the common.Marshal function to understand what errors it can return
rg "func Marshal" --type go -B 2 -A 8 | head -40Repository: QuantumNous/new-api Length of output: 423 🏁 Script executed: # Verify the data: URL sanitization pattern in task_video.go more carefully
sed -n '143,155p' controller/task_video.goRepository: QuantumNous/new-api Length of output: 416 🏁 Script executed: # Check if there are any other implementations that show how to properly handle CompletedAt
rg "CompletedAt = " relay/channel/task --type go -B 2 -A 2Repository: QuantumNous/new-api Length of output: 2751 🏁 Script executed: # Look at the vertex adaptor more carefully to understand the data: URL pattern
sed -n '410,450p' relay/channel/task/vertex/adaptor.goRepository: QuantumNous/new-api Length of output: 110 🏁 Script executed: # Check if doubao's dResp.Status could be inconsistent with originTask.Status
sed -n '248,283p' relay/channel/task/doubao/adaptor.goRepository: QuantumNous/new-api Length of output: 1202 ConvertToOpenAIVideo: don't ignore marshal errors; avoid setting CompletedAt for non-terminal states; use normalized task status instead of raw API response.
Proposed fix openAIVideo.CreatedAt = originTask.CreatedAt
-openAIVideo.CompletedAt = originTask.UpdatedAt
+if originTask.Status == model.TaskStatusSuccess || originTask.Status == model.TaskStatusFailure {
+ openAIVideo.CompletedAt = originTask.UpdatedAt
+}
openAIVideo.Model = originTask.Properties.OriginModelName
-if dResp.Status == "failed" {
+if originTask.Status == model.TaskStatusFailure {
openAIVideo.Error = &dto.OpenAIVideoError{
Message: "task failed",
Code: "failed",
}
}
-jsonData, _ := common.Marshal(openAIVideo)
-return jsonData, nil
+jsonData, err := common.Marshal(openAIVideo)
+if err != nil {
+ return nil, errors.Wrap(err, "marshal openai video failed")
+}
+return jsonData, nil🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -148,7 +148,7 @@ func GetTaskAdaptor(platform constant.TaskPlatform) channel.TaskAdaptor { | |
| return &taskvertex.TaskAdaptor{} | ||
| case constant.ChannelTypeVidu: | ||
| return &taskVidu.TaskAdaptor{} | ||
| case constant.ChannelTypeDoubaoVideo: | ||
| case constant.ChannelTypeDoubaoVideo, constant.ChannelTypeVolcEngine: | ||
| return &taskdoubao.TaskAdaptor{} | ||
|
Comment on lines
+151
to
152
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# 1) Where is GetTaskAdaptor used (which routes/endpoints)?
echo "=== GetTaskAdaptor usage ==="
rg -n 'GetTaskAdaptor\s*\(' -S
# 2) How is platform/channel_type set for task/video flows?
echo -e "\n=== Channel type / Platform assignments ==="
rg -n 'Set\("channel_type"|GetInt\("channel_type"\)|Set\("platform"|GetString\("platform"\)' -S
# 3) Confirm whether VolcEngine task routes exist and what they expect.
echo -e "\n=== VolcEngine channel/API type references ==="
rg -n 'ChannelTypeVolcEngine|APITypeVolcEngine' -S
# 4) Check the full context of the relay_adaptor.go file around line 151
echo -e "\n=== relay_adaptor.go context (lines 140-160) ==="
sed -n '140,160p' relay/relay_adaptor.goRepository: QuantumNous/new-api Length of output: 3230 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Check what "seedream" is and how it's handled
echo "=== Seedream references ==="
rg -n 'seedream' -i
# Find all task types/models associated with ChannelTypeVolcEngine
echo -e "\n=== VolcEngine channel context ==="
rg -B5 -A5 'ChannelTypeVolcEngine' relay/relay_adaptor.go controller/channel-test.go
# Check if taskdoubao handles seedream or only Doubao video
echo -e "\n=== Taskdoubao adaptor ==="
head -50 relay/channel/doubao/adaptor.go | grep -i 'seedream\|func\|adaptor' || echo "Checking adaptor structure..."
# Look for task type mappings to understand adaptor selection logic
echo -e "\n=== Task type to adaptor mapping ==="
rg -n 'GetTaskAdaptor.*case' relay/relay_adaptor.go -A30Repository: QuantumNous/new-api Length of output: 1968 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Find doubao adaptor files
echo "=== Doubao adaptor files ==="
find . -type f -name "*doubao*" | grep -E '\.(go|ts|js)$'
# Check the complete relay_adaptor.go switch statement
echo -e "\n=== Full GetTaskAdaptor function (lines 132-165) ==="
sed -n '132,165p' relay/relay_adaptor.go
# Check what task adaptors are available
echo -e "\n=== Available task adaptors ==="
ls -la relay/channel/ | grep -E '^d'
# Search for how image generation tasks are handled
echo -e "\n=== Image generation task handling ==="
rg -n 'image.*generation|images/generations' -iRepository: QuantumNous/new-api Length of output: 91 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Check relay_adaptor.go imports and full function
echo "=== relay_adaptor.go (first 180 lines) ==="
head -180 relay/relay_adaptor.go
# List all task channel adaptors
echo -e "\n=== relay/channel directory structure ==="
find relay/channel -maxdepth 1 -type d | sort
# Search for taskdoubao package/import definition
echo -e "\n=== taskdoubao references and imports ==="
rg -n 'taskdoubao\|import.*doubao' -B2 -A2Repository: QuantumNous/new-api Length of output: 6832 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Check if there's a volcengine task adaptor that should be used
echo "=== Looking for volcengine task adaptor ==="
find relay/channel -name "*volcengine*" -o -name "*volc*"
# Check what task types volcengine has in its constants
echo -e "\n=== VolcEngine models in constants ==="
cat relay/channel/volcengine/constants.go | head -30Repository: QuantumNous/new-api Length of output: 572 VolcEngine routes multiple task types—seedream (image) and seedance (video)—but this mapping misroutes them all to the Doubao video task adaptor. VolcEngine's model list includes |
||
| case constant.ChannelTypeSora, constant.ChannelTypeOpenAI: | ||
| return &tasksora.TaskAdaptor{} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security: metadata “unmarshal into payload” enables
callback_urlSSRF + overridescontent/model.json.Unmarshal(medaBytes, &r)(Line 240) allows user-suppliedmetadatato setcallback_url,content,model, etc. This is risky (SSRF/callback abuse + bypassing server-side model/content validation). Prefer an explicit allowlist of metadata fields and map them ontorequestPayload(and validateCallbackURLif it remains supported).Safer pattern sketch (allowlist-only)
📝 Committable suggestion