Split param processor and tighten Volces frame validation
This commit is contained in:
@@ -381,6 +381,128 @@ func TestParamProcessorVideoCapabilitiesNormalizeAndFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParamProcessorDowngradesReferenceImagesToFrames(t *testing.T) {
|
||||
candidate := store.RuntimeModelCandidate{
|
||||
ModelType: "image_to_video",
|
||||
Capabilities: map[string]any{
|
||||
"image_to_video": map[string]any{
|
||||
"input_first_frame": true,
|
||||
"input_first_last_frame": true,
|
||||
"input_reference_generate_single": false,
|
||||
"input_reference_generate_multiple": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
body := map[string]any{
|
||||
"model": "Seedance",
|
||||
"prompt": "animate it",
|
||||
"content": []any{
|
||||
map[string]any{"type": "text", "text": "animate it"},
|
||||
map[string]any{"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "https://example.com/first.png"}},
|
||||
map[string]any{"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "https://example.com/last.png"}},
|
||||
},
|
||||
}
|
||||
|
||||
result := preprocessRequestWithLog("videos.generations", body, candidate)
|
||||
if result.Err != nil {
|
||||
t.Fatalf("two image references should downgrade to first/last frames: %v", result.Err)
|
||||
}
|
||||
content := contentItems(result.Body["content"])
|
||||
if stringFromAny(content[1]["role"]) != "first_frame" || stringFromAny(content[2]["role"]) != "last_frame" {
|
||||
t.Fatalf("expected first/last frame downgrade, got %+v", content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParamProcessorDowngradesSingleReferenceImageToFirstFrame(t *testing.T) {
|
||||
candidate := store.RuntimeModelCandidate{
|
||||
ModelType: "image_to_video",
|
||||
Capabilities: map[string]any{
|
||||
"image_to_video": map[string]any{
|
||||
"input_first_frame": true,
|
||||
"input_first_last_frame": true,
|
||||
"input_reference_generate_single": false,
|
||||
"input_reference_generate_multiple": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
body := map[string]any{
|
||||
"model": "Seedance",
|
||||
"prompt": "animate it",
|
||||
"content": []any{
|
||||
map[string]any{"type": "text", "text": "animate it"},
|
||||
map[string]any{"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "https://example.com/first.png"}},
|
||||
},
|
||||
}
|
||||
|
||||
result := preprocessRequestWithLog("videos.generations", body, candidate)
|
||||
if result.Err != nil {
|
||||
t.Fatalf("single image reference should downgrade to first frame: %v", result.Err)
|
||||
}
|
||||
content := contentItems(result.Body["content"])
|
||||
if stringFromAny(content[1]["role"]) != "first_frame" {
|
||||
t.Fatalf("expected first frame downgrade, got %+v", content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParamProcessorRejectsUnsafeReferenceImageDowngrade(t *testing.T) {
|
||||
candidate := store.RuntimeModelCandidate{
|
||||
ModelType: "image_to_video",
|
||||
Capabilities: map[string]any{
|
||||
"image_to_video": map[string]any{
|
||||
"input_first_frame": true,
|
||||
"input_first_last_frame": false,
|
||||
"input_reference_generate_single": false,
|
||||
"input_reference_generate_multiple": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
body := map[string]any{
|
||||
"model": "Seedance",
|
||||
"prompt": "animate it",
|
||||
"content": []any{
|
||||
map[string]any{"type": "text", "text": "animate it"},
|
||||
map[string]any{"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "https://example.com/first.png"}},
|
||||
map[string]any{"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "https://example.com/last.png"}},
|
||||
},
|
||||
}
|
||||
|
||||
result := preprocessRequestWithLog("videos.generations", body, candidate)
|
||||
if result.Err == nil {
|
||||
t.Fatalf("two image references should be rejected when first/last frame is unsupported")
|
||||
}
|
||||
if len(result.Log.Changes) == 0 || result.Log.Changes[len(result.Log.Changes)-1].Action != "reject" {
|
||||
t.Fatalf("expected reject preprocessing log, got %+v", result.Log.Changes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParamProcessorRejectsVideoOrAudioReferenceDowngrade(t *testing.T) {
|
||||
candidate := store.RuntimeModelCandidate{
|
||||
ModelType: "image_to_video",
|
||||
Capabilities: map[string]any{
|
||||
"image_to_video": map[string]any{
|
||||
"input_first_frame": true,
|
||||
"input_first_last_frame": true,
|
||||
"input_reference_generate_single": false,
|
||||
"input_reference_generate_multiple": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
body := map[string]any{
|
||||
"model": "Seedance",
|
||||
"prompt": "animate it",
|
||||
"content": []any{
|
||||
map[string]any{"type": "text", "text": "animate it"},
|
||||
map[string]any{"type": "image_url", "role": "reference_image", "image_url": map[string]any{"url": "https://example.com/first.png"}},
|
||||
map[string]any{"type": "video_url", "role": "reference_video", "video_url": map[string]any{"url": "https://example.com/ref.mp4"}},
|
||||
},
|
||||
}
|
||||
|
||||
result := preprocessRequestWithLog("videos.generations", body, candidate)
|
||||
if result.Err == nil {
|
||||
t.Fatalf("video reference should be rejected instead of downgraded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParamProcessorDurationRangeRoundsFractionalSecondsUp(t *testing.T) {
|
||||
body := map[string]any{
|
||||
"model": "Seedance",
|
||||
|
||||
Reference in New Issue
Block a user