Add multipart image edit support

This commit is contained in:
2026-06-08 01:17:42 +08:00
parent b7500d81d1
commit 679bfeb9c9
9 changed files with 669 additions and 27 deletions
+114 -3
View File
@@ -208,9 +208,7 @@ func volcesImageBody(request Request) map[string]any {
if size := widthHeightSize(body); size != "" {
body["size"] = size
}
if supportsMultipleOutputs(request, request.ModelType) && body["sequential_image_generation"] == nil {
body["sequential_image_generation"] = "auto"
}
normalizeVolcesSequentialImageGeneration(body, request)
return body
}
@@ -772,6 +770,119 @@ func supportsMultipleOutputs(request Request, capabilityName string) bool {
return false
}
func normalizeVolcesSequentialImageGeneration(body map[string]any, request Request) {
options, hasOptions := volcesSequentialImageGenerationOptions(body)
normalizeVolcesSequentialMaxImages(options, request)
if _, explicit := body["sequential_image_generation"]; explicit {
return
}
if !supportsMultipleOutputs(request, request.ModelType) {
return
}
count := requestedVolcesSequentialImageCount(body, options)
if count <= 1 {
return
}
body["sequential_image_generation"] = "auto"
if !hasOptions {
options = map[string]any{}
body["sequential_image_generation_options"] = options
}
if _, ok := options["max_images"]; !ok {
options["max_images"] = count
normalizeVolcesSequentialMaxImages(options, request)
}
}
func volcesSequentialImageGenerationOptions(body map[string]any) (map[string]any, bool) {
options, ok := body["sequential_image_generation_options"].(map[string]any)
if !ok {
return nil, false
}
copied := map[string]any{}
for key, value := range options {
copied[key] = value
}
body["sequential_image_generation_options"] = copied
return copied, true
}
func normalizeVolcesSequentialMaxImages(options map[string]any, request Request) {
if options == nil {
return
}
raw, exists := options["max_images"]
if !exists {
return
}
current := int(math.Round(numericValue(raw, 0)))
minCount, maxCount := volcesSequentialMaxImagesRange(request)
adjusted := current
if minCount > 0 && adjusted < minCount {
adjusted = minCount
}
if maxCount > 0 && adjusted > maxCount {
adjusted = maxCount
}
if adjusted != current {
options["max_images"] = adjusted
}
}
func requestedVolcesSequentialImageCount(body map[string]any, options map[string]any) int {
for _, value := range []any{
valueFromMap(options, "max_images"),
body["count"],
body["n"],
body["batch_size"],
} {
count := int(math.Round(numericValue(value, 0)))
if count > 0 {
return count
}
}
return 1
}
func valueFromMap(values map[string]any, key string) any {
if values == nil {
return nil
}
return values[key]
}
func volcesSequentialMaxImagesRange(request Request) (int, int) {
minCount := 1
maxCount := 0
for _, key := range []string{request.ModelType, "image_generate", "image_edit"} {
if key == "" {
continue
}
capability, _ := request.Candidate.Capabilities[key].(map[string]any)
if capability == nil {
continue
}
if value := firstPositiveInt(capability, "output_min_images_count", "outputMinImagesCount", "min_output_images", "minOutputImages", "min_images", "minImages"); value > 0 {
minCount = value
}
if value := firstPositiveInt(capability, "output_max_images_count", "outputMaxImagesCount", "max_output_images", "maxOutputImages", "max_images", "maxImages"); value > 0 {
maxCount = value
}
return minCount, maxCount
}
return minCount, maxCount
}
func firstPositiveInt(values map[string]any, keys ...string) int {
for _, key := range keys {
value := int(math.Round(numericValue(values[key], 0)))
if value > 0 {
return value
}
}
return 0
}
func widthHeightSize(body map[string]any) string {
width := numericValue(body["width"], 0)
height := numericValue(body["height"], 0)