fix(keling): 统一 Omni 模型别名与生产绑定
ci / verify (pull_request) Successful in 21m44s
ci / verify (pull_request) Successful in 21m44s
This commit is contained in:
@@ -1070,11 +1070,15 @@ func (s *Server) createTask(kind string, compatible bool) http.Handler {
|
||||
return
|
||||
}
|
||||
}
|
||||
model := requestModelName(body)
|
||||
requestedModel := requestModelName(body)
|
||||
model := canonicalTaskModelName(kind, requestedModel)
|
||||
if model == "" {
|
||||
writeError(w, http.StatusBadRequest, "model is required")
|
||||
return
|
||||
}
|
||||
if model != requestedModel {
|
||||
body["model"] = model
|
||||
}
|
||||
if !apiKeyScopeAllowed(user, kind) {
|
||||
writeError(w, http.StatusForbidden, "api key scope does not allow this capability")
|
||||
return
|
||||
@@ -1402,6 +1406,17 @@ func requestModelName(body map[string]any) string {
|
||||
return modelNameFromValue(body["model"])
|
||||
}
|
||||
|
||||
func canonicalTaskModelName(kind string, model string) string {
|
||||
model = strings.TrimSpace(model)
|
||||
if kind != "videos.generations" {
|
||||
return model
|
||||
}
|
||||
if canonical, ok := canonicalKlingOmniModel(model); ok {
|
||||
return canonical
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
func modelNameFromValue(value any) string {
|
||||
switch typed := value.(type) {
|
||||
case string:
|
||||
|
||||
@@ -269,7 +269,7 @@ func normalizeKelingOmniRequest(input map[string]any) (map[string]any, *kelingCo
|
||||
if sound != "on" && sound != "off" {
|
||||
return nil, newKelingCompatError(http.StatusBadRequest, 1201, "sound must be on or off")
|
||||
}
|
||||
if model == "kling-o1" && sound == "on" {
|
||||
if model == klingO1Model && sound == "on" {
|
||||
return nil, newKelingCompatError(http.StatusBadRequest, 1201, "kling-video-o1 does not support generated audio; sound must be off")
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ func normalizeKelingOmniRequest(input map[string]any) (map[string]any, *kelingCo
|
||||
if duration < 3 || duration > maxDuration {
|
||||
return nil, newKelingCompatError(http.StatusBadRequest, 1201, fmt.Sprintf("duration for %s must be an integer between 3 and %d seconds", requestedModel, maxDuration))
|
||||
}
|
||||
if model == "kling-o1" && (len(images) == 0 || hasFirstFrame) && duration != 5 && duration != 10 {
|
||||
if model == klingO1Model && (len(images) == 0 || hasFirstFrame) && duration != 5 && duration != 10 {
|
||||
return nil, newKelingCompatError(http.StatusBadRequest, 1201, "kling-video-o1 text-to-video and first-frame generation only support 5 or 10 seconds")
|
||||
}
|
||||
}
|
||||
@@ -532,14 +532,14 @@ func normalizeKelingMultiPrompts(value any) ([]any, int, *kelingCompatError) {
|
||||
}
|
||||
|
||||
func kelingCompatModel(value string) (string, int, bool) {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case "kling-video-o1", "kling-o1":
|
||||
return "kling-o1", 10, true
|
||||
case "kling-v3-omni", "kling-3.0-omni":
|
||||
return "kling-3.0-omni", 15, true
|
||||
default:
|
||||
model, ok := canonicalKlingOmniModel(value)
|
||||
if !ok {
|
||||
return "", 0, false
|
||||
}
|
||||
if model == klingO1Model {
|
||||
return model, 10, true
|
||||
}
|
||||
return model, 15, true
|
||||
}
|
||||
|
||||
func kelingCompatObjectList(value any, field string) ([]map[string]any, *kelingCompatError) {
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestNormalizeKelingOmniRequestMapsOfficialFields(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("normalize Kling request: %v", err)
|
||||
}
|
||||
if normalized["model"] != "kling-3.0-omni" ||
|
||||
if normalized["model"] != "kling-v3-omni" ||
|
||||
normalized["modelType"] != "omni_video" ||
|
||||
normalized["resolution"] != "1080p" ||
|
||||
normalized["aspect_ratio"] != "9:16" ||
|
||||
@@ -49,6 +49,24 @@ func TestNormalizeKelingOmniRequestMapsOfficialFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalTaskModelNameNormalizesKelingOmniAliases(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"kling-o1": "kling-video-o1",
|
||||
"kling-video-o1": "kling-video-o1",
|
||||
"kling-3.0-omni": "kling-v3-omni",
|
||||
"kling-3-omni": "kling-v3-omni",
|
||||
"kling-v3-omni": "kling-v3-omni",
|
||||
}
|
||||
for input, expected := range tests {
|
||||
if got := canonicalTaskModelName("videos.generations", input); got != expected {
|
||||
t.Fatalf("canonicalTaskModelName(%q) = %q, want %q", input, got, expected)
|
||||
}
|
||||
}
|
||||
if got := canonicalTaskModelName("chat.completions", "kling-o1"); got != "kling-o1" {
|
||||
t.Fatalf("non-video model must not be rewritten, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeKelingOmniRequestBuildsMultiShotMedia(t *testing.T) {
|
||||
normalized, err := normalizeKelingOmniRequest(map[string]any{
|
||||
"model_name": "kling-3.0-omni",
|
||||
@@ -70,7 +88,7 @@ func TestNormalizeKelingOmniRequestBuildsMultiShotMedia(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("normalize multi-shot request: %v", err)
|
||||
}
|
||||
if normalized["model"] != "kling-3.0-omni" || normalized["duration"] != 5 || normalized["multi_shot"] != true || normalized["shot_type"] != "customize" {
|
||||
if normalized["model"] != "kling-v3-omni" || normalized["duration"] != 5 || normalized["multi_shot"] != true || normalized["shot_type"] != "customize" {
|
||||
t.Fatalf("unexpected multi-shot fields: %+v", normalized)
|
||||
}
|
||||
content, _ := normalized["content"].([]any)
|
||||
|
||||
@@ -100,9 +100,9 @@ func TestKelingOmniCompatibleHTTPFlow(t *testing.T) {
|
||||
_, err = db.CreatePlatformModel(ctx, store.CreatePlatformModelInput{
|
||||
PlatformID: platform.ID,
|
||||
CanonicalModelKey: "keling:kling-video-o1",
|
||||
ModelName: "kling-o1",
|
||||
ModelName: "kling-video-o1",
|
||||
ProviderModelName: "kling-video-o1",
|
||||
ModelAlias: "kling-o1",
|
||||
ModelAlias: "",
|
||||
ModelType: store.StringList{"omni_video", "video_generate"},
|
||||
DisplayName: "Kling O1 Compatible Test",
|
||||
Capabilities: map[string]any{
|
||||
|
||||
@@ -695,7 +695,11 @@ func klingV2Status(status string) string {
|
||||
}
|
||||
|
||||
func klingV2ProviderModel(pathModel string) (string, bool) {
|
||||
switch strings.ToLower(strings.TrimSpace(pathModel)) {
|
||||
return canonicalKlingOmniModel(pathModel)
|
||||
}
|
||||
|
||||
func canonicalKlingOmniModel(value string) (string, bool) {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case "kling-o1", "kling-video-o1":
|
||||
return klingO1Model, true
|
||||
case "kling-v3-omni", "kling-3.0-omni", "kling-3-omni":
|
||||
|
||||
Reference in New Issue
Block a user