增加wan2.5-i2i-preview图生图支持 - #2227
Conversation
WalkthroughThe changes add WAN model support to Alibaba's image editing capability. New types and handlers route WAN-based image edits through different endpoints and async behavior. A helper extracts image data from multipart forms. Conditional logic branches requests based on WAN model detection. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Adaptor as adaptor.go
participant FormExt as image.go/<br/>image_wan.go
participant Backend as ALI Backend
Client->>Adaptor: Image edit request
Adaptor->>Adaptor: isWanModel check
alt WAN Model
Adaptor->>Adaptor: Select image2image/<br/>image-synthesis endpoint
Adaptor->>Adaptor: Set X-DashScope-Async header
Adaptor->>FormExt: oaiFormEdit2WanxImageEdit
FormExt->>FormExt: getImageBase64sFromForm
FormExt->>FormExt: UnmarshalBodyReusable<br/>(WAN params)
FormExt-->>Adaptor: AliImageRequest
Adaptor->>Backend: POST (async)
Backend-->>Adaptor: Response
Adaptor->>Adaptor: Route to aliImageHandler
else Non-WAN Model
Adaptor->>Adaptor: Select multimodal-generation/<br/>generation endpoint
Adaptor->>FormExt: oaiFormEdit2AliImageEdit
FormExt->>FormExt: getImageBase64sFromForm
FormExt-->>Adaptor: AliImageRequest
Adaptor->>Backend: POST
Backend-->>Adaptor: Response
Adaptor->>Adaptor: Route to aliImageEditHandler
end
Adaptor-->>Client: Response
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly Related PRs
Suggested Reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
relay/channel/ali/image.go (1)
61-127: Fix unused parameter (compile error) and tighten file handling ingetImageBase64sFromFormTwo issues here:
- The
fieldNameparameter is never used and is shadowed by thefor fieldName, files := range mf.Fileloop variable. In Go, an unused parameter causes a compile-time error, so this function will not compile as written.- When
file.Open()orio.ReadAll(image)fail, the opened file is not closed, which can leak resources under load.A minimal fix that both uses
fieldNamemeaningfully and avoids shadowing, while improving error handling, could look like this:-func getImageBase64sFromForm(c *gin.Context, fieldName string) ([]string, error) { +func getImageBase64sFromForm(c *gin.Context, fieldName string) ([]string, error) { mf := c.Request.MultipartForm if mf == nil { if _, err := c.MultipartForm(); err != nil { return nil, fmt.Errorf("failed to parse image edit form request: %w", err) } mf = c.Request.MultipartForm } var imageFiles []*multipart.FileHeader var exists bool - // First check for standard "image" field - if imageFiles, exists = mf.File["image"]; !exists || len(imageFiles) == 0 { - // If not found, check for "image[]" field - if imageFiles, exists = mf.File["image[]"]; !exists || len(imageFiles) == 0 { - // If still not found, iterate through all fields to find any that start with "image[" + if fieldName == "" { + fieldName = "image" + } + + // First check for standard fieldName + if imageFiles, exists = mf.File[fieldName]; !exists || len(imageFiles) == 0 { + // If not found, check for "<field>[]" + if imageFiles, exists = mf.File[fieldName+"[]"]; !exists || len(imageFiles) == 0 { + // If still not found, iterate through all fields to find any that start with "<field>[" foundArrayImages := false - for fieldName, files := range mf.File { - if strings.HasPrefix(fieldName, "image[") && len(files) > 0 { + for formFieldName, files := range mf.File { + if strings.HasPrefix(formFieldName, fieldName+"[") && len(files) > 0 { foundArrayImages = true imageFiles = append(imageFiles, files...) } } @@ - // 获取base64编码的图片 + // 获取base64编码的图片 var imageBase64s []string for _, file := range imageFiles { - image, err := file.Open() - if err != nil { - return nil, errors.New("failed to open image file") - } - - // 读取文件内容 - imageData, err := io.ReadAll(image) - if err != nil { - return nil, errors.New("failed to read image file") - } + image, err := file.Open() + if err != nil { + return nil, fmt.Errorf("failed to open image file: %w", err) + } + // Ensure the file is always closed + func() { + defer image.Close() + + // 读取文件内容 + imageData, err := io.ReadAll(image) + if err != nil { + // propagate error out of the loop + imageBase64s = nil + return + } + + // 获取MIME类型 + mimeType := http.DetectContentType(imageData) + + // 编码为base64 + base64Data := base64.StdEncoding.EncodeToString(imageData) + + // 构造data URL格式 + dataURL := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data) + imageBase64s = append(imageBase64s, dataURL) + }() + if imageBase64s == nil { + return nil, fmt.Errorf("failed to read image file") + } - - // 获取MIME类型 - mimeType := http.DetectContentType(imageData) - - // 编码为base64 - base64Data := base64.StdEncoding.EncodeToString(imageData) - - // 构造data URL格式 - dataURL := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data) - imageBase64s = append(imageBase64s, dataURL) - image.Close() } return imageBase64s, nil }(Exact error-propagation pattern inside the loop is flexible; the key points are: actually using
fieldName, avoiding shadowing, and always closing the opened file even on error.)
🧹 Nitpick comments (2)
relay/channel/ali/dto.go (1)
115-126: WAN DTOs look correct; consider enforcing API constraints earlier
WanImageInput/WanImageParametersmatch the WAN API shape and should serialize fine. To fail fast and return clearer errors before hitting Ali, you might consider:
- Validating
len(Images) <= 2when building the request.- Clamping or validating
Nto the documented[1,4]range (and possiblySeed/Strengthranges) in the converter.This can live either in the WAN converter or as small helper validation functions around these DTOs.
relay/channel/ali/image_wan.go (1)
37-39:isWanModelis simple and works; consider tightening matching if more models are added
strings.Contains(modelName, "wan")is a pragmatic way to route WAN models likewan2.5-i2i-preview. If additional non-WAN models containing"wan"are introduced later, you might want to switch to a stricter check (e.g., exact prefix match, allowlist, or regex) to avoid misrouting.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
relay/channel/ali/adaptor.go(4 hunks)relay/channel/ali/dto.go(1 hunks)relay/channel/ali/image.go(2 hunks)relay/channel/ali/image_wan.go(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
relay/channel/ali/image.go (3)
relay/common/relay_info.go (1)
RelayInfo(76-123)dto/openai_image.go (1)
ImageRequest(14-34)relay/channel/ali/dto.go (1)
AliImageRequest(94-99)
relay/channel/ali/image_wan.go (3)
relay/common/relay_info.go (1)
RelayInfo(76-123)relay/channel/ali/dto.go (3)
AliImageRequest(94-99)WanImageInput(115-119)WanImageParameters(121-126)common/gin.go (1)
UnmarshalBodyReusable(33-58)
🔇 Additional comments (2)
relay/channel/ali/image.go (1)
129-137: Refactor ofoaiFormEdit2AliImageEditto use shared helper looks goodThe switch to
getImageBase64sFromFormand wrapping its error with context keeps the edit path consistent with the WAN path and improves error messages. The rest of the function remains unchanged in behavior.relay/channel/ali/adaptor.go (1)
50-54: WAN edit routing, headers, and handlers are consistent; verify alignment with Ali WAN APIThe new WAN-specific branches look coherent:
- URL selection (Lines 50-54): WAN models under
RelayModeImagesEditsnow target/api/v1/services/aigc/image2image/image-synthesis, while non-WAN edits still use/multimodal-generation/generation. Good separation of the two APIs.- Headers (Lines 78-82): For edit requests with WAN models you enable
X-DashScope-Async, matching the async handling used elsewhere, and forceContent-Type: application/jsonfor the outgoing request body.- Request conversion (Lines 117-119): WAN edit requests are converted via
oaiFormEdit2WanxImageEdit, while the existing multipart vs JSON path is preserved for non-WAN edits.- Response handling (Lines 174-178): WAN edits now reuse
aliImageHandler, which performs async task polling; non-WAN edits remain onaliImageEditHandler.Assuming Ali’s
image2image/image-synthesisendpoint for WAN models indeed uses the same async task contract as the existing text2image path, this wiring should behave correctly. It would be good to double-check against the latest WAN API docs or a live call that:
- WAN edit responses return a task id compatible with
aliImageHandler/asyncTaskWait, and- Async mode really is required (or at least supported) when
X-DashScope-Async=enableis set.Also applies to: 78-82, 117-119, 174-178
| func oaiFormEdit2WanxImageEdit(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (*AliImageRequest, error) { | ||
| var err error | ||
| var imageRequest AliImageRequest | ||
| imageRequest.Model = request.Model | ||
| imageRequest.ResponseFormat = request.ResponseFormat | ||
| wanInput := WanImageInput{ | ||
| Prompt: request.Prompt, | ||
| } | ||
|
|
||
| if err := common.UnmarshalBodyReusable(c, &wanInput); err != nil { | ||
| return nil, err | ||
| } | ||
| if wanInput.Images, err = getImageBase64sFromForm(c, "image"); err != nil { | ||
| return nil, fmt.Errorf("get image base64s from form failed: %w", err) | ||
| } | ||
| wanParams := WanImageParameters{ | ||
| N: int(request.N), | ||
| } | ||
| imageRequest.Input = wanInput | ||
| imageRequest.Parameters = wanParams | ||
| return &imageRequest, nil | ||
| } |
There was a problem hiding this comment.
Unused info parameter in oaiFormEdit2WanxImageEdit causes compile failure
In Go, function parameters must be used or explicitly ignored. Here info *relaycommon.RelayInfo is never referenced in the body, which will cause a compile-time error.
A minimal fix is to mark the parameter as intentionally unused:
-func oaiFormEdit2WanxImageEdit(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (*AliImageRequest, error) {
+func oaiFormEdit2WanxImageEdit(c *gin.Context, _ *relaycommon.RelayInfo, request dto.ImageRequest) (*AliImageRequest, error) {A couple of smaller improvements you may also want to consider in this function:
-
Wrap the
UnmarshalBodyReusableerror with context so callers know which step failed, similar to how you wrap the image helper error:if err := common.UnmarshalBodyReusable(c, &wanInput); err != nil { return nil, fmt.Errorf("unmarshal WAN input from request failed: %w", err) }
-
Optionally validate
len(wanInput.Images)andwanParams.Nagainst the documented WAN limits (e.g., max 2 images, N in [1,4]) before sending the request.
🤖 Prompt for AI Agents
In relay/channel/ali/image_wan.go around lines 14 to 35, the parameter info
*relaycommon.RelayInfo is unused and causes a compile error; mark it as
intentionally unused by adding a blank identifier assignment (e.g. _ = info) at
the top of the function, change the UnmarshalBodyReusable error return to wrap
the error with context (e.g. return nil, fmt.Errorf("unmarshal WAN input from
request failed: %w", err)), keep the existing wrapped error for
getImageBase64sFromForm, and (optionally) validate wanInput.Images length and
wanParams.N against expected WAN limits before returning the request.
…i-preview 增加wan2.5-i2i-preview图生图支持
官方文档:
https://help.aliyun.com/zh/model-studio/wan2-5-image-edit-api-reference?spm=a2c4g.11186623.help-menu-2400256.d_2_2_5.334275acmnKHP4&scm=20140722.H_2982258._.OR_help-T_cn~zh-V_1#4c9724d501qsq支持模型:
wan2.5-i2i-preview请求示例:
Summary by CodeRabbit