fix(models): 模型目录 - 保留人工能力并按声明发送视频分辨率
This commit is contained in:
1 parent
050a95c1d5
commit
c2352fd9b6
11 files changed
+218
-48
No files matched your search
@@ -67,6 +67,7 @@ type VideoCapabilityConfig struct {
|
||||
|
||||
type VideoReferenceConfig struct {
|
||||
PromptMaxChars int `json:"promptMaxChars"`
|
||||
MinImages int `json:"minImages"`
|
||||
MaxImages int `json:"maxImages"`
|
||||
MaxImageBytes int64 `json:"maxImageBytes"`
|
||||
MaxVideos int `json:"maxVideos"`
|
||||
@@ -154,7 +155,7 @@ func defaultImageSizeValues() []string {
|
||||
|
||||
func DefaultModelCapabilityConfigForModel(protocol string, modelName string) *ModelCapabilityConfig {
|
||||
video := &VideoCapabilityConfig{
|
||||
References: VideoReferenceConfig{PromptMaxChars: 1000, MaxImages: 9, MaxImageBytes: 30 * 1024 * 1024, MaxVideos: 0, MaxVideoBytes: 0, MaxVideoDuration: 0, MaxAudios: 0, MaxAudioBytes: 0, MaxAudioDuration: 0},
|
||||
References: VideoReferenceConfig{PromptMaxChars: 1000, MinImages: 0, MaxImages: 9, MaxImageBytes: 30 * 1024 * 1024, MaxVideos: 0, MaxVideoBytes: 0, MaxVideoDuration: 0, MaxAudios: 0, MaxAudioBytes: 0, MaxAudioDuration: 0},
|
||||
Duration: VideoDurationConfig{Selection: "range", Min: 1, Max: 15, Step: 1, Default: 6},
|
||||
Ratios: []string{"16:9", "9:16", "1:1", "4:3", "3:4", "21:9"},
|
||||
DefaultRatio: "16:9",
|
||||
@@ -277,11 +278,14 @@ func validateVideoCapabilityConfig(value *VideoCapabilityConfig) error {
|
||||
if value.References.PromptMaxChars < 1 || value.References.PromptMaxChars > 1000000 {
|
||||
return BadAuthRequest("提示词最大字符数必须在 1-1000000 之间")
|
||||
}
|
||||
for name, number := range map[string]int{"最大图片引用数": value.References.MaxImages, "最大视频引用数": value.References.MaxVideos, "最大音频引用数": value.References.MaxAudios} {
|
||||
for name, number := range map[string]int{"最少图片引用数": value.References.MinImages, "最大图片引用数": value.References.MaxImages, "最大视频引用数": value.References.MaxVideos, "最大音频引用数": value.References.MaxAudios} {
|
||||
if number < 0 || number > 100 {
|
||||
return BadAuthRequest(name + "必须在 0-100 之间")
|
||||
}
|
||||
}
|
||||
if value.References.MinImages > value.References.MaxImages {
|
||||
return BadAuthRequest("最少图片引用数不能超过最大图片引用数")
|
||||
}
|
||||
if value.References.MaxImageBytes < 0 || value.References.MaxVideoBytes < 0 || value.References.MaxAudioBytes < 0 || value.References.MaxVideoDuration < 0 || value.References.MaxAudioDuration < 0 {
|
||||
return BadAuthRequest("引用素材限制不能小于 0")
|
||||
}
|
||||
@@ -291,8 +295,12 @@ func validateVideoCapabilityConfig(value *VideoCapabilityConfig) error {
|
||||
if len(value.Ratios) == 0 || strings.TrimSpace(value.DefaultRatio) == "" || !containsCapabilityString(value.Ratios, value.DefaultRatio) {
|
||||
return BadAuthRequest("请至少配置一个画面比例,并选择默认比例")
|
||||
}
|
||||
if len(value.Resolutions) == 0 || strings.TrimSpace(value.DefaultResolution) == "" || !containsCapabilityString(value.Resolutions, value.DefaultResolution) {
|
||||
return BadAuthRequest("请至少配置一个输出分辨率,并选择默认分辨率")
|
||||
if len(value.Resolutions) == 0 {
|
||||
if strings.TrimSpace(value.DefaultResolution) != "" {
|
||||
return BadAuthRequest("未配置输出分辨率时不能设置默认分辨率")
|
||||
}
|
||||
} else if strings.TrimSpace(value.DefaultResolution) == "" || !containsCapabilityString(value.Resolutions, value.DefaultResolution) {
|
||||
return BadAuthRequest("默认输出分辨率必须属于支持值")
|
||||
}
|
||||
if len(value.Operations) == 0 || strings.TrimSpace(value.DefaultOperation) == "" || !containsCapabilityString(value.Operations, value.DefaultOperation) {
|
||||
return BadAuthRequest("请至少配置一个生成模式,并选择默认模式")
|
||||
@@ -377,6 +385,9 @@ func validateVideoTask(profile *VideoCapabilityConfig, input canvasGenerationInp
|
||||
if len(input.ReferenceImages) > profile.References.MaxImages || len(input.ReferenceVideos) > profile.References.MaxVideos || len(input.ReferenceAudios) > profile.References.MaxAudios {
|
||||
return BadAuthRequest("参考素材数量超过当前模型限制")
|
||||
}
|
||||
if len(input.ReferenceImages) < profile.References.MinImages {
|
||||
return BadAuthRequest(fmt.Sprintf("当前视频模型至少需要 %d 张参考图", profile.References.MinImages))
|
||||
}
|
||||
for _, media := range input.ReferenceImages {
|
||||
if profile.References.MaxImageBytes > 0 && media.Bytes > profile.References.MaxImageBytes {
|
||||
return BadAuthRequest("参考图片文件超过当前模型大小限制")
|
||||
|
||||
@@ -116,3 +116,32 @@ func TestValidateVideoTaskIgnoresGlobalResolutionWhenCatalogDeclaresNone(t *test
|
||||
t.Fatalf("validateVideoTask() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeVideoCapabilityAllowsOmittedResolution(t *testing.T) {
|
||||
profile := DefaultModelCapabilityConfigForModel("newapi-channel-2", "endpoint-video").Video
|
||||
profile.Resolutions = nil
|
||||
profile.DefaultResolution = ""
|
||||
|
||||
result, err := NormalizeModelCapabilityConfig("video", "newapi-channel-2", &ModelCapabilityConfig{Version: 1, Video: profile})
|
||||
if err != nil {
|
||||
t.Fatalf("NormalizeModelCapabilityConfig() error = %v", err)
|
||||
}
|
||||
if result.Video == nil || len(result.Video.Resolutions) != 0 || result.Video.DefaultResolution != "" {
|
||||
t.Fatalf("normalized video resolution = %#v", result.Video)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateVideoTaskRequiresDeclaredMinimumImages(t *testing.T) {
|
||||
profile := DefaultModelCapabilityConfigForModel("newapi-channel-2", "image-required-video").Video
|
||||
profile.References.MinImages = 1
|
||||
profile.References.MaxImages = 2
|
||||
profile.Operations = []string{"image_to_video"}
|
||||
profile.DefaultOperation = "image_to_video"
|
||||
|
||||
err := validateVideoTask(profile, canvasGenerationInput{
|
||||
Config: providerConfig{Model: "image-required-video", VideoSeconds: "6", Size: "16:9", VQuality: "720"},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "至少需要 1 张参考图") {
|
||||
t.Fatalf("validateVideoTask() error = %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1973,7 +1973,10 @@ func newAPIChannel2VideoRequestBody(input canvasGenerationInput) (newAPIVideoReq
|
||||
seconds = 6
|
||||
}
|
||||
ratio := normalizeNewAPIChannel2Ratio(input.Config.Size, modelName)
|
||||
resolution := normalizeNewAPIChannel2Resolution(input.Config.VQuality, modelName)
|
||||
resolution := videoResolutionNameRequest(input.VideoCapability, input.Config.VQuality)
|
||||
if modelName == "grok-video-1.5-1080p" {
|
||||
resolution = "1080p"
|
||||
}
|
||||
body := newAPIVideoRequest{
|
||||
Model: input.Config.Model,
|
||||
Prompt: strings.TrimSpace(input.Prompt),
|
||||
|
||||
@@ -19,7 +19,7 @@ type newAPIVideoRequest struct {
|
||||
Prompt string `json:"prompt"`
|
||||
Seconds string `json:"seconds"`
|
||||
AspectRatio string `json:"aspect_ratio"`
|
||||
Resolution string `json:"resolution"`
|
||||
Resolution string `json:"resolution,omitempty"`
|
||||
GenerateAudio *bool `json:"generate_audio,omitempty"`
|
||||
ImageURLs []string `json:"image_urls,omitempty"`
|
||||
VideoURLs []string `json:"video_urls,omitempty"`
|
||||
|
||||
@@ -1278,9 +1278,12 @@ func TestRunNewAPIChannel2VideoTaskDownloadsTemporaryResult(t *testing.T) {
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
profile := DefaultModelCapabilityConfigForModel("newapi-channel-2", "grok-image-video").Video
|
||||
profile.Resolutions = []string{"720p"}
|
||||
profile.DefaultResolution = "720p"
|
||||
result, err := runVideoTask(context.Background(), canvasGenerationInput{
|
||||
Prompt: "make it move",
|
||||
Config: providerConfig{BaseURL: server.URL, APIKey: "test-key", Model: "grok-image-video", InterfaceType: "newapi-channel-2", VideoSeconds: "15", Size: "720x1280", VQuality: "high"},
|
||||
Config: providerConfig{BaseURL: server.URL, APIKey: "test-key", Model: "grok-image-video", InterfaceType: "newapi-channel-2", VideoSeconds: "15", Size: "720x1280", VQuality: "720"},
|
||||
ReferenceImages: []providerMedia{
|
||||
{ID: "image-1", DataURL: testReferenceImageDataURL},
|
||||
{ID: "image-2", DataURL: testReferenceImageDataURL},
|
||||
@@ -1288,6 +1291,7 @@ func TestRunNewAPIChannel2VideoTaskDownloadsTemporaryResult(t *testing.T) {
|
||||
ReferenceVideos: []providerMedia{{ID: "video-1", URL: server.URL + "/reference.mp4"}},
|
||||
ReferenceAudios: []providerMedia{{ID: "audio-1", URL: server.URL + "/reference.mp3"}},
|
||||
Metadata: map[string]interface{}{"videoEditOperation": "image_to_video"},
|
||||
VideoCapability: profile,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("runVideoTask() error = %v", err)
|
||||
@@ -1354,6 +1358,50 @@ func TestNewAPIChannel2SingleImageModelsRequireOneReference(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAPIChannel2SendsOnlyDeclaredResolution(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
model string
|
||||
quality string
|
||||
resolutions []string
|
||||
want string
|
||||
}{
|
||||
{name: "catalog omits resolution", model: "endpoint-video", quality: "720"},
|
||||
{name: "declared 2K alias", model: "declared-video", quality: "2K", resolutions: []string{"1440p"}, want: "1440p"},
|
||||
{name: "fixed grok 1080p model", model: "grok-video-1.5-1080p", quality: "720", want: "1080p"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
profile := DefaultModelCapabilityConfigForModel("newapi-channel-2", test.model).Video
|
||||
profile.Resolutions = test.resolutions
|
||||
profile.DefaultResolution = ""
|
||||
input := canvasGenerationInput{
|
||||
Config: providerConfig{Model: test.model, VideoSeconds: "6", Size: "16:9", VQuality: test.quality},
|
||||
VideoCapability: profile,
|
||||
}
|
||||
if strings.HasPrefix(test.model, "grok-video-1.5") {
|
||||
input.ReferenceImages = []providerMedia{{ID: "image-1", DataURL: testReferenceImageDataURL}}
|
||||
}
|
||||
|
||||
body, err := newAPIChannel2VideoRequestBody(input)
|
||||
if err != nil {
|
||||
t.Fatalf("newAPIChannel2VideoRequestBody() error = %v", err)
|
||||
}
|
||||
if body.Resolution != test.want {
|
||||
t.Fatalf("resolution = %q, want %q", body.Resolution, test.want)
|
||||
}
|
||||
mapped, err := requestAsMap(body)
|
||||
if err != nil {
|
||||
t.Fatalf("requestAsMap() error = %v", err)
|
||||
}
|
||||
_, hasResolution := mapped["resolution"]
|
||||
if hasResolution != (test.want != "") {
|
||||
t.Fatalf("resolution presence = %v, want %v; body = %#v", hasResolution, test.want != "", mapped)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAPIChannel2RejectsAudioWithoutReferenceVideo(t *testing.T) {
|
||||
_, err := newAPIChannel2VideoRequestBody(canvasGenerationInput{
|
||||
Config: providerConfig{Model: "grok-image-video", VideoSeconds: "6"},
|
||||
|
||||
Reference in new issue
Block a user