Restore MiniMax media routing parity across image and video flows
The server now keeps portable image ratio normalization, enforces documented Seedream size presets, routes MiniMax img2img through generations-style payloads, and submits Hailuo video variants through the video generations API with proper reference-image handling. Constraint: The deployed test chain relies on popiartServer as the lowest-risk place to adapt provider-specific routing without broad newapi changes Rejected: Expand newapi relay modes for every MiniMax image/video branch | larger blast radius than needed for the verified server-managed flows Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep MiniMax-specific media routing in popiartServer unless upstream newapi adapters are upgraded to the same contract Tested: go test ./internal/server -run TestInferRouteKeyForModelRecognizesViduAsVideo|TestGenerate|TestResolve|TestSubmitMiniMaxVideoTask|TestExecuteImageToImageJobUsesGenerationsPathForMiniMax|TestExecuteImageToVideoJob|TestResolveVideoReferencesSupportsImagesArray Not-tested: Full PopiNewAPI unit suite (blocked earlier by external module checksum drift in local checkout)
This commit is contained in:
@@ -129,7 +129,7 @@ func TestGenerateSeedreamImageRefsUsesImagesGenerationsEndpoint(t *testing.T) {
|
||||
|
||||
refs, usage, err := client.generateImageRefs(context.Background(), "sk-test", "seedream-4-5-251128", map[string]any{
|
||||
"prompt": "draw a golden retriever in the park",
|
||||
"size": "1024x1024",
|
||||
"size": "2K",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("generateImageRefs: %v", err)
|
||||
@@ -141,7 +141,7 @@ func TestGenerateSeedreamImageRefsUsesImagesGenerationsEndpoint(t *testing.T) {
|
||||
t.Fatalf("unexpected auth header: %q", gotAuth)
|
||||
}
|
||||
if gotBody["size"] != "2K" {
|
||||
t.Fatalf("expected normalized 2K size, got %#v", gotBody["size"])
|
||||
t.Fatalf("expected preset 2K size, got %#v", gotBody["size"])
|
||||
}
|
||||
if gotBody["response_format"] != "url" {
|
||||
t.Fatalf("expected default response_format=url, got %#v", gotBody["response_format"])
|
||||
@@ -184,7 +184,7 @@ func TestGenerateEditedImageRefsUsesSeedreamImagesGenerationsEndpoint(t *testing
|
||||
|
||||
refs, usage, err := client.generateEditedImageRefs(context.Background(), "sk-test", "seedream-4-5-251128", map[string]any{
|
||||
"prompt": "edit this image into a dusk scene",
|
||||
"size": "1024x1536",
|
||||
"size": "2K",
|
||||
}, imageEditReference{
|
||||
Filename: "source.jpg",
|
||||
ContentType: "image/jpeg",
|
||||
@@ -201,7 +201,7 @@ func TestGenerateEditedImageRefsUsesSeedreamImagesGenerationsEndpoint(t *testing
|
||||
t.Fatalf("unexpected auth header: %q", gotAuth)
|
||||
}
|
||||
if gotBody["size"] != "2K" {
|
||||
t.Fatalf("expected normalized 2K size, got %#v", gotBody["size"])
|
||||
t.Fatalf("expected preset 2K size, got %#v", gotBody["size"])
|
||||
}
|
||||
if gotBody["image"] != "https://example.com/reference.jpg" {
|
||||
t.Fatalf("expected image URL payload, got %#v", gotBody["image"])
|
||||
@@ -218,9 +218,15 @@ func TestResolveGeminiAspectRatioFromSize(t *testing.T) {
|
||||
if got := resolveGeminiAspectRatio(map[string]any{"size": "1792x1024"}); got != "16:9" {
|
||||
t.Fatalf("expected 16:9, got %q", got)
|
||||
}
|
||||
if got := resolveGeminiAspectRatio(map[string]any{"size": "2520x1080"}); got != "21:9" {
|
||||
t.Fatalf("expected 21:9 from exact size, got %q", got)
|
||||
}
|
||||
if got := resolveGeminiAspectRatio(map[string]any{"aspect_ratio": "4:5"}); got != "4:5" {
|
||||
t.Fatalf("expected explicit aspect ratio to win, got %q", got)
|
||||
}
|
||||
if got := resolveGeminiAspectRatio(map[string]any{"aspect_ratio": "4x5"}); got != "4:5" {
|
||||
t.Fatalf("expected normalized 4:5, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveGeminiImageSizeFromResolutionOrSize(t *testing.T) {
|
||||
@@ -230,16 +236,127 @@ func TestResolveGeminiImageSizeFromResolutionOrSize(t *testing.T) {
|
||||
if got := resolveGeminiImageSize(map[string]any{"size": "1024x1024"}); got != "1K" {
|
||||
t.Fatalf("expected 1K from square preset, got %q", got)
|
||||
}
|
||||
if got := resolveGeminiImageSize(map[string]any{"size": "2520x1080"}); got != "2K" {
|
||||
t.Fatalf("expected 2K bucket for 2520x1080, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSeedreamImageSizeUsesSupportedPresetOrFallback(t *testing.T) {
|
||||
if got := resolveSeedreamImageSize("doubao-seedream-4-5-251128", map[string]any{"resolution": "4K"}); got != "4K" {
|
||||
got, err := resolveSeedreamImageSize("doubao-seedream-4-5-251128", map[string]any{"resolution": "4K"})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error for 4K preset, got %v", err)
|
||||
}
|
||||
if got != "4K" {
|
||||
t.Fatalf("expected 4K for Seedream 4.5, got %q", got)
|
||||
}
|
||||
if got := resolveSeedreamImageSize("doubao-seedream-4-5-251128", map[string]any{"size": "1024x1536"}); got != "2K" {
|
||||
t.Fatalf("expected 2K fallback for pixel size, got %q", got)
|
||||
if _, err := resolveSeedreamImageSize("doubao-seedream-4-5-251128", map[string]any{"size": "1024x1536"}); err == nil {
|
||||
t.Fatal("expected exact pixel size to be rejected for Seedream")
|
||||
}
|
||||
if got := resolveSeedreamImageSize("doubao-seedream-5-0-260128", map[string]any{"resolution": "3K"}); got != "3K" {
|
||||
got, err = resolveSeedreamImageSize("doubao-seedream-5-0-260128", map[string]any{"resolution": "3K"})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error for 3K preset, got %v", err)
|
||||
}
|
||||
if got != "3K" {
|
||||
t.Fatalf("expected 3K for Seedream 5.0, got %q", got)
|
||||
}
|
||||
if _, err := resolveSeedreamImageSize("doubao-seedream-4-5-251128", map[string]any{"size": "2048x1152"}); err == nil {
|
||||
t.Fatal("expected exact size passthrough to be rejected for Seedream")
|
||||
}
|
||||
if _, err := resolveSeedreamImageSize("doubao-seedream-4-5-251128", map[string]any{"aspect_ratio": "4:5"}); err == nil {
|
||||
t.Fatal("expected aspect_ratio to be rejected for Seedream")
|
||||
}
|
||||
got, err = resolveSeedreamImageSize("doubao-seedream-4-5-251128", map[string]any{})
|
||||
if err != nil {
|
||||
t.Fatalf("expected default 2K without explicit size, got %v", err)
|
||||
}
|
||||
if got != "2K" {
|
||||
t.Fatalf("expected default 2K, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateMiniMaxImageRefsUsesImagesGenerationsEndpoint(t *testing.T) {
|
||||
var gotPath string
|
||||
var gotBody map[string]any
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotPath = r.URL.Path
|
||||
if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil {
|
||||
t.Fatalf("decode request body: %v", err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{
|
||||
"created": 1711234567,
|
||||
"data": [{"url":"https://example.com/minimax.png"}]
|
||||
}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := &newAPIClient{
|
||||
baseURL: strings.TrimRight(srv.URL, "/"),
|
||||
httpClient: srv.Client(),
|
||||
}
|
||||
|
||||
refs, _, err := client.generateImageRefs(context.Background(), "sk-test", "image-01", map[string]any{
|
||||
"prompt": "draw a portrait",
|
||||
"aspect_ratio": "4x5",
|
||||
"response_format": "b64_json",
|
||||
"prompt_optimizer": true,
|
||||
"watermark": false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("generateImageRefs: %v", err)
|
||||
}
|
||||
if gotPath != "/v1/images/generations" {
|
||||
t.Fatalf("unexpected path: %s", gotPath)
|
||||
}
|
||||
if gotBody["aspect_ratio"] != "4:5" {
|
||||
t.Fatalf("expected normalized aspect ratio 4:5, got %#v", gotBody["aspect_ratio"])
|
||||
}
|
||||
if gotBody["response_format"] != "b64_json" {
|
||||
t.Fatalf("expected b64_json response format, got %#v", gotBody["response_format"])
|
||||
}
|
||||
if gotBody["prompt_optimizer"] != true {
|
||||
t.Fatalf("expected prompt_optimizer=true, got %#v", gotBody["prompt_optimizer"])
|
||||
}
|
||||
if len(refs) != 1 || refs[0].URL != "https://example.com/minimax.png" {
|
||||
t.Fatalf("unexpected refs: %#v", refs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateMiniMaxEditedImageRefsSendsStandardImageField(t *testing.T) {
|
||||
var gotBody map[string]any
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil {
|
||||
t.Fatalf("decode request body: %v", err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{
|
||||
"created": 1711234567,
|
||||
"data": [{"url":"https://example.com/minimax-edit.png"}]
|
||||
}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := &newAPIClient{
|
||||
baseURL: strings.TrimRight(srv.URL, "/"),
|
||||
httpClient: srv.Client(),
|
||||
}
|
||||
|
||||
_, _, err := client.generateEditedImageRefs(context.Background(), "sk-test", "image-01", map[string]any{
|
||||
"prompt": "turn it into watercolor",
|
||||
"size": "832x1248",
|
||||
"response_format": "url",
|
||||
}, imageEditReference{
|
||||
URL: "https://example.com/reference.jpg",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("generateEditedImageRefs: %v", err)
|
||||
}
|
||||
if gotBody["image"] != "https://example.com/reference.jpg" {
|
||||
t.Fatalf("expected standard image field, got %#v", gotBody["image"])
|
||||
}
|
||||
if gotBody["size"] != "832x1248" {
|
||||
t.Fatalf("expected size passthrough for minimax mapping, got %#v", gotBody["size"])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user