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
+103 -3
View File
@@ -992,7 +992,8 @@ func TestVolcesClientImageEditUsesGenerationEndpoint(t *testing.T) {
var gotAuth string
var gotModel string
var gotImage string
var gotSequential string
var gotSequential any
var gotSequentialPresent bool
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
gotAuth = r.Header.Get("Authorization")
@@ -1002,7 +1003,7 @@ func TestVolcesClientImageEditUsesGenerationEndpoint(t *testing.T) {
}
gotModel, _ = body["model"].(string)
gotImage, _ = body["image"].(string)
gotSequential, _ = body["sequential_image_generation"].(string)
gotSequential, gotSequentialPresent = body["sequential_image_generation"]
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "img-volces-edit",
"created": 123,
@@ -1036,7 +1037,7 @@ func TestVolcesClientImageEditUsesGenerationEndpoint(t *testing.T) {
if gotPath != "/images/generations" || gotAuth != "Bearer volces-key" {
t.Fatalf("unexpected request path=%s auth=%s", gotPath, gotAuth)
}
if gotModel != "doubao-seedream-4-0-250828" || gotImage != "https://example.com/source.png" || gotSequential != "auto" {
if gotModel != "doubao-seedream-4-0-250828" || gotImage != "https://example.com/source.png" || gotSequentialPresent {
t.Fatalf("unexpected body model=%s image=%s sequential=%s", gotModel, gotImage, gotSequential)
}
if response.Result["id"] != "img-volces-edit" {
@@ -1044,6 +1045,105 @@ func TestVolcesClientImageEditUsesGenerationEndpoint(t *testing.T) {
}
}
func TestVolcesClientImageEditEnablesSequentialForRequestedMultipleImages(t *testing.T) {
var gotSequential string
var gotMaxImages float64
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body map[string]any
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("decode request: %v", err)
}
gotSequential, _ = body["sequential_image_generation"].(string)
options, _ := body["sequential_image_generation_options"].(map[string]any)
gotMaxImages = numericValue(options["max_images"], 0)
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "img-volces-edit",
"data": []any{map[string]any{"url": "https://example.com/out.png"}},
})
}))
defer server.Close()
_, err := (VolcesClient{HTTPClient: server.Client()}).Run(context.Background(), Request{
Kind: "images.edits",
ModelType: "image_edit",
Body: map[string]any{
"prompt": "make variants",
"image": "https://example.com/source.png",
"n": 3,
},
Candidate: store.RuntimeModelCandidate{
BaseURL: server.URL,
ProviderModelName: "doubao-seedream-4-0-250828",
Credentials: map[string]any{"apiKey": "volces-key"},
Capabilities: map[string]any{
"image_edit": map[string]any{
"output_multiple_images": true,
"output_max_images_count": 4,
},
},
},
})
if err != nil {
t.Fatalf("run volces image edit: %v", err)
}
if gotSequential != "auto" || gotMaxImages != 3 {
t.Fatalf("unexpected sequential settings mode=%s max_images=%v", gotSequential, gotMaxImages)
}
}
func TestVolcesClientImageEditPreservesExplicitSequentialDisabledAndClampsMaxImages(t *testing.T) {
var gotSequential string
var gotMaxImages float64
var gotOtherOption string
options := map[string]any{"max_images": 99, "other": "keep"}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body map[string]any
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("decode request: %v", err)
}
gotSequential, _ = body["sequential_image_generation"].(string)
gotOptions, _ := body["sequential_image_generation_options"].(map[string]any)
gotMaxImages = numericValue(gotOptions["max_images"], 0)
gotOtherOption, _ = gotOptions["other"].(string)
_ = json.NewEncoder(w).Encode(map[string]any{
"id": "img-volces-edit",
"data": []any{map[string]any{"url": "https://example.com/out.png"}},
})
}))
defer server.Close()
_, err := (VolcesClient{HTTPClient: server.Client()}).Run(context.Background(), Request{
Kind: "images.edits",
ModelType: "image_edit",
Body: map[string]any{
"prompt": "make variants",
"image": "https://example.com/source.png",
"sequential_image_generation": "disabled",
"sequential_image_generation_options": options,
},
Candidate: store.RuntimeModelCandidate{
BaseURL: server.URL,
ProviderModelName: "doubao-seedream-4-0-250828",
Credentials: map[string]any{"apiKey": "volces-key"},
Capabilities: map[string]any{
"image_edit": map[string]any{
"output_multiple_images": true,
"output_max_images_count": 4,
},
},
},
})
if err != nil {
t.Fatalf("run volces image edit: %v", err)
}
if gotSequential != "disabled" || gotMaxImages != 4 || gotOtherOption != "keep" {
t.Fatalf("unexpected sequential settings mode=%s max_images=%v other=%s", gotSequential, gotMaxImages, gotOtherOption)
}
if numericValue(options["max_images"], 0) != 99 {
t.Fatalf("request options should not be mutated: %+v", options)
}
}
func TestVolcesClientVideoSubmitsAndPollsTask(t *testing.T) {
var submitPath string
var pollPath string