feat(kling): 接入 Omni 视频兼容接口

This commit is contained in:
2026-07-22 00:25:05 +08:00
parent b04a7d9d3d
commit 5a71643099
10 changed files with 1886 additions and 7 deletions
+79 -7
View File
@@ -338,8 +338,11 @@ func kelingVideoPayload(ctx context.Context, request Request) (map[string]any, s
if value, ok := body["cfg_scale"]; ok && numericValue(value, 0) > 0 {
payload["cfg_scale"] = value
}
if boolValue(body, "audio") || boolValue(body, "output_audio") {
payload["sound"] = "on"
if sound, ok := kelingSoundSetting(body); ok {
if sound == "on" && !kelingSupportsGeneratedSound(request.Candidate) {
return nil, "", &ClientError{Code: "invalid_parameter", Message: "kling-video-o1 does not support generated audio", StatusCode: http.StatusBadRequest, Retryable: false}
}
payload["sound"] = sound
}
if mode := kelingModeByResolution(firstNonEmptyStringValue(body, "resolution", "size")); mode != "" {
payload["mode"] = mode
@@ -432,7 +435,7 @@ func (c KelingClient) kelingOmniPayload(ctx context.Context, request Request, to
watermarkEnabled = boolValue(watermarkInfo, "enabled")
}
payload := map[string]any{
"model_name": upstreamModelName(request.Candidate),
"model_name": kelingOmniUpstreamModelName(request.Candidate),
"mode": kelingModeByResolution(firstNonEmptyStringValue(body, "resolution", "size")),
"watermark_info": map[string]any{"enabled": watermarkEnabled},
"negative_prompt": strings.TrimSpace(stringFromAny(body["negative_prompt"])),
@@ -458,8 +461,13 @@ func (c KelingClient) kelingOmniPayload(ctx context.Context, request Request, to
if voices := mapListFromAny(body["voice_list"]); len(voices) > 0 {
payload["voice_list"] = voices
}
if (boolValue(body, "audio") || boolValue(body, "output_audio")) && !hasVideo {
payload["sound"] = "on"
if sound, ok := kelingSoundSetting(body); ok {
if sound == "on" && !kelingSupportsGeneratedSound(request.Candidate) {
return nil, nil, &ClientError{Code: "invalid_parameter", Message: "kling-video-o1 does not support generated audio", StatusCode: http.StatusBadRequest, Retryable: false}
}
if !hasVideo {
payload["sound"] = sound
}
}
if multiShot {
payload["multi_shot"] = true
@@ -728,6 +736,18 @@ func kelingIsOmniRequest(request Request) bool {
request.Candidate.Capabilities["omni"] != nil
}
func kelingOmniUpstreamModelName(candidate store.RuntimeModelCandidate) string {
model := strings.TrimSpace(upstreamModelName(candidate))
switch strings.ToLower(model) {
case "kling-o1":
return "kling-video-o1"
case "kling-3.0-omni":
return "kling-v3-omni"
default:
return model
}
}
func kelingIs30TurboRequest(request Request) bool {
switch strings.ToLower(strings.TrimSpace(upstreamModelName(request.Candidate))) {
case "kling-3.0-turbo", "kling-v3-turbo", "kling-3-0-turbo":
@@ -1073,6 +1093,54 @@ func kelingModeByResolution(resolution string) string {
}
}
func kelingSoundSetting(body map[string]any) (string, bool) {
if sound := strings.ToLower(strings.TrimSpace(stringFromAny(body["sound"]))); sound == "on" || sound == "off" {
return sound, true
}
for _, key := range []string{"audio", "output_audio", "generate_audio"} {
if enabled, ok := kelingBoolFieldValue(body, key); ok {
if enabled {
return "on", true
}
return "off", true
}
}
return "", false
}
func kelingSupportsGeneratedSound(candidate store.RuntimeModelCandidate) bool {
switch strings.ToLower(strings.TrimSpace(upstreamModelName(candidate))) {
case "kling-o1", "kling-video-o1":
return false
default:
return true
}
}
func kelingWatermarkEnabled(body map[string]any) bool {
if enabled, ok := kelingBoolFieldValue(body, "watermark"); ok {
return enabled
}
info := mapFromAny(body["watermark_info"])
if info == nil {
return false
}
enabled, _ := kelingBoolFieldValue(info, "enabled")
return enabled
}
func kelingBoolFieldValue(body map[string]any, key string) (bool, bool) {
if body == nil {
return false, false
}
value, ok := body[key]
if !ok {
return false, false
}
typed, ok := value.(bool)
return typed, ok
}
func kelingCameraControl(body map[string]any) map[string]any {
cameraControl := strings.TrimSpace(stringFromAny(body["camera_control"]))
if cameraControl == "" {
@@ -1182,7 +1250,7 @@ func kelingVideoSuccessResult(request Request, upstreamTaskID string, raw map[st
if id := strings.TrimSpace(stringFromAny(video["id"])); id != "" {
item["id"] = id
}
if duration := intFromAny(video["duration"]); duration > 0 {
if duration := firstPresent(video["duration"]); duration != nil && strings.TrimSpace(stringFromAny(duration)) != "" {
item["duration"] = duration
}
if watermarkURL := strings.TrimSpace(stringFromAny(video["watermark_url"])); watermarkURL != "" {
@@ -1194,11 +1262,15 @@ func kelingVideoSuccessResult(request Request, upstreamTaskID string, raw map[st
if created == 0 {
created = int(nowUnix())
}
modelName := upstreamModelName(request.Candidate)
if kelingIsOmniRequest(request) {
modelName = kelingOmniUpstreamModelName(request.Candidate)
}
return map[string]any{
"id": upstreamTaskID,
"object": "video.generation",
"created": created,
"model": upstreamModelName(request.Candidate),
"model": modelName,
"status": "succeeded",
"upstream_task_id": upstreamTaskID,
"data": items,