Skip to content

Gemini Veo3.1[AI Studio]增加图生视频支持 - #2315

Merged
creamlike1024 merged 2 commits into
QuantumNous:mainfrom
feitianbubu:pr/gemini-veo3.1-i2v
Nov 27, 2025
Merged

Gemini Veo3.1[AI Studio]增加图生视频支持#2315
creamlike1024 merged 2 commits into
QuantumNous:mainfrom
feitianbubu:pr/gemini-veo3.1-i2v

Conversation

@feitianbubu

@feitianbubu feitianbubu commented Nov 26, 2025

Copy link
Copy Markdown
Member
  1. 支持图生视频, 通过参数images传图像url
  2. 增加生成失败的判断
    请求示例
curl http://localhost:3000/v1/videos \
  --request POST \
  --header 'Content-Type: multipart/form-data' \
  --form 'prompt=动起来' \
  --form 'model=veo-3.1-generate-preview' \
  --form 'images=https://ark-project.tos-cn-beijing.volces.com/doc_image/seedream4_imagesToimages_1.png'

返回结果

{
  "id": "bW9kZWxzL3Zlby0zLjEtZ2VuZXJhdGUtcHJldmlldy9vcGVyYXRpb25zL3RncmRkYWN6c2hjag",
  "task_id": "bW9kZWxzL3Zlby0zLjEtZ2VuZXJhdGUtcHJldmlldy9vcGVyYXRpb25zL3RncmRkYWN6c2hjag",
  "object": "video",
  "model": "veo-3.1-generate-preview",
  "status": "",
  "progress": 0,
  "created_at": 1764165250
}
image image

Summary by CodeRabbit

Release Notes

  • Refactor

    • Streamlined video generation request structure for improved handling
  • Bug Fixes

    • Enhanced media filtering detection and improved error reporting for video generation requests
    • Better validation and processing of image inputs in video generation workflows

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Nov 26, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Refactored Gemini video generation adapter types and payloads: renamed GeminiVideo* types to Video*, introduced Image and VideoRequest structures for improved image handling, updated validation to use TaskActionGenerate, and enhanced result parsing with RAI filtering context.

Changes

Cohort / File(s) Change Summary
Type Refactoring and Renames
relay/channel/task/gemini/adaptor.go
Renamed GeminiVideoGenerationConfigVideoGenerationConfig, GeminiVideoPayloadVideoPayload; removed GeminiVideoRequest and replaced with new aggregation using VideoRequest
New Public Structures
relay/channel/task/gemini/adaptor.go
Introduced Image struct with BytesBase64Encoded and MimeType fields; introduced VideoRequest struct with Prompt, Image *Image, LastFrame *Image; introduced VideoPayload struct with Instances []VideoRequest and Parameters VideoGenerationConfig
Request Construction Updates
relay/channel/task/gemini/adaptor.go
Updated BuildRequestBody to construct new VideoPayload shape with VideoRequest(s); enhanced metadata handling by marshaling/unmarshaling request metadata into VideoPayload.Parameters
Validation and Action Handling
relay/channel/task/gemini/adaptor.go
Changed ValidateRequestAndSetAction to use constant TaskActionGenerate instead of TaskActionTextGenerate
Result Parsing and Image Conversion
relay/channel/task/gemini/adaptor.go
Updated ParseTaskResult to handle absence of GeneratedSamples as failure with RaiMediaFilteredReasons; added private method convertImage(imageStr string) *Image for data URL, base64, and remote URL conversion
Response Metadata Expansion
relay/channel/task/gemini/adaptor.go
Extended response payload structures to include RaiMediaFilteredCount and RaiMediaFilteredReasons for capturing filtering context

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Focus areas: Verify correctness of field mappings between old and new type structures (particularly Image and LastFrame handling in VideoRequest)
  • Attention needed: Validate that the convertImage helper correctly processes all image input formats (data URLs, base64, remote URLs)
  • Critical review: Confirm ParseTaskResult logic properly handles both filtered and unfiltered response paths with RaiMediaFilteredReasons
  • Validation check: Ensure transition from TaskActionTextGenerate to TaskActionGenerate is semantically correct for video generation flows

Possibly related PRs

  • PR #2075: Direct predecessor that introduced the Gemini video adaptor types now being refactored and renamed in this PR
  • PR #1788: Related work on multi-image TaskSubmitReq consumption and TaskActionGenerate validation pattern changes affecting task adaptors
  • PR #2140: Concurrent modifications to Gemini task adaptor payload structures and result parsing with updated video URI extraction and model mapping

Suggested reviewers

  • seefs001
  • creamlike1024

Poem

🐰 Hop! The video types take flight,
From Gemini to Video—what a sight!
Images dance in nested delight,
New request shapes, parsing made right,
RAI filters catching the light!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding image-to-video generation support for Gemini Veo3.1, which aligns with the primary modifications to support image inputs in video generation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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 and usage tips.

@feitianbubu feitianbubu changed the title veo3.1增加图生视频支持 Gemini Veo3.1增加图生视频支持 Nov 26, 2025
@feitianbubu feitianbubu changed the title Gemini Veo3.1增加图生视频支持 Gemini Veo3.1[AI Studio]增加图生视频支持 Nov 26, 2025

@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: 2

🧹 Nitpick comments (1)
relay/channel/task/gemini/adaptor.go (1)

310-315: Data URL parsing may silently produce incorrect results.

If a data URL doesn't contain ;base64, (e.g., data:image/png,<raw-data>), the split produces only one part but img retains the full data URL as BytesBase64Encoded, which is invalid.

Consider adding validation:

 	if strings.HasPrefix(imageStr, "data:image/") {
 		parts := strings.Split(imageStr, ";base64,")
 		if len(parts) == 2 {
 			img.MimeType = strings.TrimPrefix(parts[0], "data:")
 			img.BytesBase64Encoded = parts[1]
+		} else {
+			// Invalid data URL format, return nil or error
+			return nil
 		}
+		return img
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95a7749 and f7d6c36.

📒 Files selected for processing (1)
  • relay/channel/task/gemini/adaptor.go (6 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-08T17:12:43.157Z
Learnt from: RedwindA
Repo: QuantumNous/new-api PR: 1537
File: relay/gemini_handler.go:330-342
Timestamp: 2025-08-08T17:12:43.157Z
Learning: In the new-api repository, the `GeminiEmbeddingHandler` function in `relay/gemini_handler.go` is designed specifically for native Gemini embedding requests and therefore does not require the `ConvertGeminiRequest` step that is used in the chat handler. The embedding requests are already in the native Gemini format and don't need conversion.

Applied to files:

  • relay/channel/task/gemini/adaptor.go
📚 Learning: 2025-08-26T09:59:00.337Z
Learnt from: Sh1n3zZ
Repo: QuantumNous/new-api PR: 1659
File: relay/relay_task.go:285-305
Timestamp: 2025-08-26T09:59:00.337Z
Learning: In controller/task_video.go, the redactVideoResponseBody function sanitizes video task responses by removing bytesBase64Encoded fields and truncating base64 strings to 256 characters to prevent large binary data from being stored in task.Data.

Applied to files:

  • relay/channel/task/gemini/adaptor.go
🧬 Code graph analysis (1)
relay/channel/task/gemini/adaptor.go (5)
relay/common/relay_utils.go (1)
  • ValidateBasicTaskRequest (204-228)
constant/task.go (1)
  • TaskActionGenerate (14-14)
model/task.go (2)
  • TaskStatusFailure (37-37)
  • TaskStatusSuccess (38-38)
setting/system_setting/system_setting_old.go (1)
  • ServerAddress (3-3)
service/image.go (1)
  • GetImageFromUrl (69-117)
🔇 Additional comments (3)
relay/channel/task/gemini/adaptor.go (3)

27-52: LGTM!

The type definitions are well-structured. Using pointers for optional Image and LastFrame fields ensures proper JSON serialization with omitempty.


80-82: LGTM!

Adding RaiMediaFilteredCount and RaiMediaFilteredReasons fields enables proper failure reporting when content is filtered by Gemini's RAI system.


106-108: The repository clone operation failed, which prevents me from executing shell scripts to verify the constant definitions and usage patterns in the codebase.

To complete the verification, I need you to provide one of the following:

  1. Run the shell script provided in the review comment on your local environment and share the output:

    # Verify usage of TaskActionGenerate vs TaskActionTextGenerate for video tasks
    echo "=== Checking TaskActionGenerate usage ==="
    rg -n "TaskActionGenerate" --type go -C2
    
    echo ""
    echo "=== Checking TaskActionTextGenerate usage ==="
    rg -n "TaskActionTextGenerate" --type go -C2
  2. Alternatively, provide context about:

    • Where TaskActionGenerate and TaskActionTextGenerate are defined (typically in constant/task.go based on the review comment)
    • How these constants are used in other task adaptor files
    • Whether other video-related task handlers use TaskActionGenerate or TaskActionTextGenerate

Once I have this information, I can complete the verification and provide the rewritten review comment.

Comment on lines +150 to 155
if len(req.Images) > 0 {
body.Instances[0].Image = a.convertImage(req.Images[0])
}
if len(req.Images) > 1 {
body.Instances[0].LastFrame = a.convertImage(req.Images[1])
}

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.

⚠️ Potential issue | 🟠 Major

Silent failure when image fetch fails.

convertImage silently returns an Image with the original URL string as BytesBase64Encoded when service.GetImageFromUrl fails. This will likely cause the upstream API to reject the request with a confusing error.

Consider returning an error from convertImage or at least logging the failure:

-func (a *TaskAdaptor) convertImage(imageStr string) *Image {
+func (a *TaskAdaptor) convertImage(imageStr string) (*Image, error) {
 	if strings.TrimSpace(imageStr) == "" {
-		return nil
+		return nil, nil
 	}
-	img := &Image{
-		MimeType:           "image/png",
-		BytesBase64Encoded: imageStr,
-	}
 	if strings.HasPrefix(imageStr, "data:image/") {
 		parts := strings.Split(imageStr, ";base64,")
 		if len(parts) == 2 {
-			img.MimeType = strings.TrimPrefix(parts[0], "data:")
-			img.BytesBase64Encoded = parts[1]
+			return &Image{
+				MimeType:           strings.TrimPrefix(parts[0], "data:"),
+				BytesBase64Encoded: parts[1],
+			}, nil
 		}
+		return nil, fmt.Errorf("invalid data URL format")
 	} else if strings.HasPrefix(imageStr, "http") {
 		mimeType, data, err := service.GetImageFromUrl(imageStr)
-		if err == nil {
-			img.MimeType = mimeType
-			img.BytesBase64Encoded = data
+		if err != nil {
+			return nil, fmt.Errorf("fetch image failed: %w", err)
 		}
+		return &Image{MimeType: mimeType, BytesBase64Encoded: data}, nil
 	}
-	return img
+	// Assume raw base64
+	return &Image{MimeType: "image/png", BytesBase64Encoded: imageStr}, nil
 }

Then update BuildRequestBody to handle the error:

 	if len(req.Images) > 0 {
-		body.Instances[0].Image = a.convertImage(req.Images[0])
+		img, err := a.convertImage(req.Images[0])
+		if err != nil {
+			return nil, errors.Wrap(err, "convert image failed")
+		}
+		body.Instances[0].Image = img
 	}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In relay/channel/task/gemini/adaptor.go around lines 150-155, convertImage
currently swallows failures from service.GetImageFromUrl and returns an Image
with the original URL in BytesBase64Encoded causing upstream rejections; change
convertImage to return (Image, error) (or at minimum log the specific fetch
error), propagate and handle that error in BuildRequestBody so you either return
the error to the caller or skip/omit the image field when fetching fails, and
update the two call sites in BuildRequestBody to check the error and act
accordingly (return a wrapped error with context or skip setting
body.Instances[0].Image/LastFrame).

Comment on lines +261 to 274
if len(op.Response.GenerateVideoResponse.GeneratedSamples) == 0 {
ti.Status = model.TaskStatusFailure
ti.Reason = fmt.Sprintf("no generated video url found: %s", strings.Join(op.Response.GenerateVideoResponse.RaiMediaFilteredReasons, "; "))
} else {
if uri := op.Response.GenerateVideoResponse.GeneratedSamples[0].Video.URI; uri != "" {
ti.RemoteUrl = uri
}
ti.Status = model.TaskStatusSuccess
}

ti.Progress = "100%"
taskID := encodeLocalTaskID(op.Name)
ti.TaskID = taskID
ti.Url = fmt.Sprintf("%s/v1/videos/%s/content", system_setting.ServerAddress, taskID)
return ti, 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.

⚠️ Potential issue | 🟡 Minor

URL is set even when generation fails.

When no GeneratedSamples exist (RAI filtered), the status is set to FAILURE but ti.Url is still populated on line 273. This URL would point to non-existent content, potentially confusing clients.

Consider only setting the URL on success:

 	if len(op.Response.GenerateVideoResponse.GeneratedSamples) == 0 {
 		ti.Status = model.TaskStatusFailure
 		ti.Reason = fmt.Sprintf("no generated video url found: %s", strings.Join(op.Response.GenerateVideoResponse.RaiMediaFilteredReasons, "; "))
+		ti.Progress = "100%"
 	} else {
 		if uri := op.Response.GenerateVideoResponse.GeneratedSamples[0].Video.URI; uri != "" {
 			ti.RemoteUrl = uri
 		}
 		ti.Status = model.TaskStatusSuccess
+		ti.Progress = "100%"
+		taskID := encodeLocalTaskID(op.Name)
+		ti.TaskID = taskID
+		ti.Url = fmt.Sprintf("%s/v1/videos/%s/content", system_setting.ServerAddress, taskID)
 	}
-	ti.Progress = "100%"
-	taskID := encodeLocalTaskID(op.Name)
-	ti.TaskID = taskID
-	ti.Url = fmt.Sprintf("%s/v1/videos/%s/content", system_setting.ServerAddress, taskID)
 	return ti, nil
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if len(op.Response.GenerateVideoResponse.GeneratedSamples) == 0 {
ti.Status = model.TaskStatusFailure
ti.Reason = fmt.Sprintf("no generated video url found: %s", strings.Join(op.Response.GenerateVideoResponse.RaiMediaFilteredReasons, "; "))
} else {
if uri := op.Response.GenerateVideoResponse.GeneratedSamples[0].Video.URI; uri != "" {
ti.RemoteUrl = uri
}
ti.Status = model.TaskStatusSuccess
}
ti.Progress = "100%"
taskID := encodeLocalTaskID(op.Name)
ti.TaskID = taskID
ti.Url = fmt.Sprintf("%s/v1/videos/%s/content", system_setting.ServerAddress, taskID)
return ti, nil
if len(op.Response.GenerateVideoResponse.GeneratedSamples) == 0 {
ti.Status = model.TaskStatusFailure
ti.Reason = fmt.Sprintf("no generated video url found: %s", strings.Join(op.Response.GenerateVideoResponse.RaiMediaFilteredReasons, "; "))
ti.Progress = "100%"
} else {
if uri := op.Response.GenerateVideoResponse.GeneratedSamples[0].Video.URI; uri != "" {
ti.RemoteUrl = uri
}
ti.Status = model.TaskStatusSuccess
ti.Progress = "100%"
taskID := encodeLocalTaskID(op.Name)
ti.TaskID = taskID
ti.Url = fmt.Sprintf("%s/v1/videos/%s/content", system_setting.ServerAddress, taskID)
}
return ti, nil
🤖 Prompt for AI Agents
In relay/channel/task/gemini/adaptor.go around lines 261 to 274, the code sets
ti.Url unconditionally even when generation failed; move the ti.Url assignment
so it is only set when the task status is success (i.e., inside the branch where
GeneratedSamples exist and after setting ti.TaskID), and ensure that on failure
ti.Url is left empty (or explicitly cleared) so clients won't receive a pointer
to non-existent content.

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.

2 participants