diff --git a/backend/internal/service/provider.go b/backend/internal/service/provider.go index 5be30a771..8491e4589 100644 --- a/backend/internal/service/provider.go +++ b/backend/internal/service/provider.go @@ -2388,6 +2388,12 @@ func queryProtocolAdapterVideoTask(ctx context.Context, input canvasGenerationIn } func protocolRequestFromInput(input canvasGenerationInput) protocol.GenerationRequest { + resolution := strings.TrimSpace(input.Config.VQuality) + if input.Mode == "video" { + if declared := videoResolutionNameRequest(input.VideoCapability, resolution); declared != "" { + resolution = declared + } + } request := protocol.GenerationRequest{ Capability: protocol.Capability(input.Mode), Model: input.Config.Model, @@ -2397,7 +2403,7 @@ func protocolRequestFromInput(input canvasGenerationInput) protocol.GenerationRe Videos: protocolMediaReferences(input.ReferenceVideos, "video"), Audios: protocolMediaReferences(input.ReferenceAudios, "audio"), AspectRatio: input.Config.Size, - Resolution: input.Config.VQuality, + Resolution: resolution, Quality: input.Config.Quality, GenerateAudio: parseBool(input.Config.VideoGenerateAudio, false), Watermark: parseBool(input.Config.VideoWatermark, false), diff --git a/backend/internal/service/provider_test.go b/backend/internal/service/provider_test.go index 7c20e93ac..1da377a84 100644 --- a/backend/internal/service/provider_test.go +++ b/backend/internal/service/provider_test.go @@ -2037,6 +2037,63 @@ func TestProtocolRequestPreservesVideoImageIDsAndRoles(t *testing.T) { } } +func TestProtocolRequestRestoresDeclaredVideoResolutionEnum(t *testing.T) { + tests := []struct { + name string + quality string + resolutions []string + want string + }{ + {name: "lowercase suffix", quality: "480", resolutions: []string{"480p", "720p"}, want: "480p"}, + {name: "provider casing", quality: "1080", resolutions: []string{"720P", "1080P"}, want: "1080P"}, + {name: "opaque enum", quality: "768p竖", resolutions: []string{"768p竖", "768p横"}, want: "768p竖"}, + {name: "unmatched custom value", quality: "native", resolutions: []string{"720p"}, want: "native"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + request := protocolRequestFromInput(canvasGenerationInput{ + Mode: "video", + Config: providerConfig{VQuality: test.quality}, + VideoCapability: &VideoCapabilityConfig{Resolutions: test.resolutions}, + }) + if request.Resolution != test.want || request.Output.Resolution != test.want { + t.Fatalf("resolution = %q, output resolution = %q, want %q", request.Resolution, request.Output.Resolution, test.want) + } + }) + } +} + +func TestVolcengineArkDeclarativeRequestUsesResolutionSuffix(t *testing.T) { + profile := DefaultModelCapabilityConfigForModel("volcengine-ark-video", "doubao-seedance-2-0-fast-260128").Video + request := protocolRequestFromInput(canvasGenerationInput{ + Mode: "video", + Prompt: "make it move", + Config: providerConfig{ + InterfaceType: "volcengine-ark-video", + Model: "doubao-seedance-2-0-fast-260128", + Size: "16:9", + VQuality: "480", + VideoSeconds: "4", + }, + VideoCapability: profile, + }) + adapter, ok := loadOfficialFallbackRegistry().Resolve("volcengine-ark-video") + if !ok { + t.Fatal("Volcengine Ark declarative adapter is unavailable") + } + spec, err := adapter.BuildCreate(context.Background(), protocol.RequestContext{Request: request}) + if err != nil { + t.Fatal(err) + } + body, ok := spec.Body.(map[string]any) + if !ok { + t.Fatalf("body = %#v", spec.Body) + } + if body["resolution"] != "480p" { + t.Fatalf("resolution = %#v, want 480p", body["resolution"]) + } +} + func TestNewAPIChannel1VideoBodyRejectsInlineMedia(t *testing.T) { _, err := newAPIChannel1VideoBody(canvasGenerationInput{ Prompt: "make it move",