Skip to content

支持可灵使用openai sdk生成视频#2004

Merged
Calcium-Ion merged 3 commits into
QuantumNous:mainfrom
feitianbubu:pr/openai-sdk-kling
Oct 11, 2025
Merged

支持可灵使用openai sdk生成视频#2004
Calcium-Ion merged 3 commits into
QuantumNous:mainfrom
feitianbubu:pr/openai-sdk-kling

Conversation

@feitianbubu
Copy link
Copy Markdown
Contributor

@feitianbubu feitianbubu commented Oct 10, 2025

import OpenAI from 'openai';
视频生成: await client.videos.create(requestParams);
视频查询: await client.videos.retrieve(taskId);

Summary by CodeRabbit

  • New Features
    • Submit video generation requests using either JSON or multipart form.
    • Unified, OpenAI-compatible video response schema including status, progress, timestamps, duration, size, metadata, and structured errors.
    • Consistent video fetch-by-ID responses across providers using the unified schema.
    • Enhanced request validation for prompt/model and automatic action selection when input references are detected.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 10, 2025

Walkthrough

Adds JSON support for video submission parsing, introduces OpenAIVideo/OpenAIVideoError types, defines an OpenAIVideoConverter interface, implements converters for Sora and Kling adaptors, and updates /v1/videos/{id} fetch to use adaptor-based conversion instead of raw task data.

Changes

Cohort / File(s) Summary
Middleware request parsing
middleware/distributor.go
For POST /v1/videos, branch by Content-Type: parse multipart form for multipart/form-data, or unmarshal JSON for application/json to extract model; unify error handling.
Relay common types & validation
relay/common/relay_info.go, relay/common/relay_utils.go
Adds OpenAIVideo and OpenAIVideoError types. Updates ValidateMultipartDirect to support multipart and JSON bodies, validate prompt/model, and determine action based on input reference.
Channel interface & adaptors
relay/channel/adapter.go, relay/channel/task/sora/adaptor.go, relay/channel/task/kling/adaptor.go
Introduces OpenAIVideoConverter interface. Implements ConvertToOpenAIVideo for Sora (JSON to struct) and Kling (map Kling response to OpenAIVideo with progress parsing and error payload).
Relay task response building
relay/relay_task.go
Updates videoFetchByIDRespBodyBuilder: resolve adaptor, require OpenAIVideoConverter, convert Task to OpenAIVideo, and return marshaled result; handle errors and not-implemented cases.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Middleware as Middleware
  participant Validator as Validation (relay/common)
  participant TaskSvc as Task Service

  rect rgb(245,245,255)
    note over Client,Middleware: POST /v1/videos
    Client->>Middleware: Request (Content-Type)
    alt Content-Type = multipart/form-data
      Middleware->>Validator: Parse multipart form
    else Content-Type = application/json
      Middleware->>Validator: Unmarshal JSON body
    end
    Validator-->>Middleware: prompt, model, hasInputReference, action
  end
  Middleware-->>Client: Continue processing (not shown)
Loading
sequenceDiagram
  autonumber
  actor Client
  participant Relay as relay_task.go
  participant Resolver as GetTaskAdaptor
  participant Adaptor as Channel Adaptor
  participant Converter as OpenAIVideoConverter

  rect rgb(245,255,245)
    note over Client,Relay: GET /v1/videos/{id}
    Client->>Relay: Fetch by ID
    Relay->>Resolver: Resolve adaptor by platform
    Resolver-->>Relay: adaptor
    alt Adaptor implements OpenAIVideoConverter
      Relay->>Converter: ConvertToOpenAIVideo(task)
      Converter-->>Relay: OpenAIVideo or error
      alt success
        Relay-->>Client: 200 JSON(OpenAIVideo)
      else error
        Relay-->>Client: convert_to_openai_video_failed
      end
    else not implemented
      Relay-->>Client: not_implemented
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • seefs001
  • creamlike1024
  • xyfacai

Poem

I hop through headers, sniff Content-Type,
Multipart or JSON? I get it just right.
Tasks turn to videos, neat and bright,
Adaptors translate in moonlit light.
With little paws, I stitch the flow—
From prompt to playback, off we go! 🐇🎬

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely and accurately describes the primary change of adding support for the KeLing channel to use the OpenAI SDK for video generation, matching the main functionality introduced across the PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
relay/channel/task/kling/adaptor.go (2)

373-380: Consider handling progress parsing errors more explicitly.

The convertProgress function returns 0 when parsing fails, which is indistinguishable from legitimate 0% progress. While logging a warning is good, consider whether the caller should be notified of parse failures.

If you want to distinguish between valid 0% and parse errors, consider returning a default like -1 or wrapping in an optional type:

 	convertProgress := func(progress string) int {
 		progress = strings.TrimSuffix(progress, "%")
 		p, err := strconv.Atoi(progress)
 		if err != nil {
 			logger.Warnf("convert progress failed, progress: %s, err: %v", progress, err)
+			return -1 // or handle as a special case
 		}
 		return p
 	}

404-409: Reconsider the error attachment condition.

The check klingResp.Code != 0 && klingResp.Message != "" may miss error cases where:

  • Code is 0 but Message indicates an error
  • Code is non-zero but Message is empty

Consider a more robust check:

-	if klingResp.Code != 0 && klingResp.Message != "" {
+	if klingResp.Code != 0 || klingResp.Message != "" {
 		openAIVideo.Error = &relaycommon.OpenAIVideoError{
 			Message: klingResp.Message,
 			Code:    fmt.Sprintf("%d", klingResp.Code),
 		}
 	}

Or if Code == 0 always means success:

 	if klingResp.Code != 0 && klingResp.Message != "" {
 		openAIVideo.Error = &relaycommon.OpenAIVideoError{
-			Message: klingResp.Message,
+			Message: lo.Ternary(klingResp.Message != "", klingResp.Message, "Unknown error"),
 			Code:    fmt.Sprintf("%d", klingResp.Code),
 		}
 	}
relay/common/relay_utils.go (1)

108-161: Consider refactoring to reduce complexity.

The function now handles two different content types with branching logic, which increases cognitive complexity. While functional, consider extracting the parsing logic into separate helper functions for better maintainability.

Example refactor:

func parseMultipartVideo(c *gin.Context) (prompt string, hasInputReference bool, err *dto.TaskError) {
	// ... multipart parsing logic
}

func parseJSONVideo(c *gin.Context) (prompt string, hasInputReference bool, err *dto.TaskError) {
	// ... JSON parsing logic
}

func ValidateMultipartDirect(c *gin.Context, info *RelayInfo) *dto.TaskError {
	contentType := c.GetHeader("Content-Type")
	var prompt string
	var hasInputReference bool
	var taskErr *dto.TaskError

	if strings.HasPrefix(contentType, "multipart/form-data") {
		prompt, hasInputReference, taskErr = parseMultipartVideo(c)
	} else {
		prompt, hasInputReference, taskErr = parseJSONVideo(c)
	}
	
	if taskErr != nil {
		return taskErr
	}
	
	// ... rest of validation
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df0ae92 and 5f36e32.

📒 Files selected for processing (7)
  • middleware/distributor.go (1 hunks)
  • relay/channel/adapter.go (2 hunks)
  • relay/channel/task/kling/adaptor.go (2 hunks)
  • relay/channel/task/sora/adaptor.go (1 hunks)
  • relay/common/relay_info.go (1 hunks)
  • relay/common/relay_utils.go (1 hunks)
  • relay/relay_task.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
relay/channel/adapter.go (2)
model/task.go (3)
  • Task (23-42)
  • Task (231-235)
  • Task (237-241)
relay/common/relay_info.go (1)
  • OpenAIVideo (554-569)
middleware/distributor.go (1)
common/gin.go (2)
  • ParseMultipartFormReusable (118-139)
  • UnmarshalBodyReusable (31-52)
relay/channel/task/sora/adaptor.go (2)
relay/common/relay_info.go (1)
  • OpenAIVideo (554-569)
common/json.go (1)
  • Unmarshal (8-10)
relay/common/relay_utils.go (2)
common/gin.go (2)
  • ParseMultipartFormReusable (118-139)
  • UnmarshalBodyReusable (31-52)
relay/common/relay_info.go (1)
  • TaskSubmitReq (484-493)
relay/channel/task/kling/adaptor.go (3)
relay/channel/adapter.go (1)
  • TaskAdaptor (33-52)
model/task.go (3)
  • Task (23-42)
  • Task (231-235)
  • Task (237-241)
relay/common/relay_info.go (2)
  • OpenAIVideo (554-569)
  • OpenAIVideoError (570-573)
relay/relay_task.go (3)
relay/relay_adaptor.go (1)
  • GetTaskAdaptor (122-146)
service/error.go (2)
  • TaskErrorWrapperLocal (133-137)
  • TaskErrorWrapper (139-155)
relay/channel/adapter.go (1)
  • OpenAIVideoConverter (54-56)
🔇 Additional comments (5)
relay/channel/adapter.go (1)

54-56: LGTM!

The interface is well-defined with a clear contract for converting task data to OpenAI video format.

relay/channel/task/sora/adaptor.go (1)

188-195: LGTM! Implementation correctly unmarshals stored OpenAIVideo data.

The implementation is clean and properly handles errors. The approach of unmarshaling task.Data directly assumes it was stored in OpenAIVideo format during task creation.

relay/relay_task.go (1)

376-384: LGTM! Proper use of type assertion and converter interface.

The type assertion with ok check and the conversion flow are implemented correctly.

relay/common/relay_info.go (1)

554-573: LGTM! Well-structured OpenAI video types.

The data structures are well-defined with appropriate JSON tags. The Progress field as int is clear, though note that conversion from string progress (e.g., "75%") must be handled by the converters.

middleware/distributor.go (1)

177-194: Potential index out of bounds when accessing form.Value["model"].

Line 186 accesses form.Value["model"][0] without first checking if the slice has elements, which could cause a panic if the model field is present but empty.

Apply this diff to add a length check:

 			if form != nil {
-				if values, ok := form.Value["model"]; ok && len(values) > 0 {
+				if values, ok := form.Value["model"]; ok && len(values) > 0 && values[0] != "" {
 					modelRequest.Model = values[0]
 				}
 			}

Or alternatively, validate that the model value is non-empty:

 			if form != nil {
 				if values, ok := form.Value["model"]; ok && len(values) > 0 {
-					modelRequest.Model = values[0]
+					if values[0] != "" {
+						modelRequest.Model = values[0]
+					}
 				}
 			}

Likely an incorrect or invalid review comment.

"strings"
"time"

"github.com/bytedance/gopkg/util/logger"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify the new external logger dependency.

The import of github.com/bytedance/gopkg/util/logger introduces a new external dependency. Ensure this aligns with the project's dependency management strategy.

Run the following script to check if this logger is used elsewhere in the codebase:


🏁 Script executed:

#!/bin/bash
# Check usage of bytedance logger across the codebase
rg -n "github.com/bytedance/gopkg/util/logger" --type=go

Length of output: 80


Address logger dependency and error handling

  • Confirm import of github.com/bytedance/gopkg/util/logger aligns with project policy; it’s only used in adaptor.go.
  • convertProgress returns 0 on parse failure without signaling an error—return an error or log failures to avoid silent zero progress.
  • Error attachment check if resp.Code != 0 && resp.Message != "" misses message-only errors—use || instead.
🤖 Prompt for AI Agents
In relay/channel/task/kling/adaptor.go around line 14, ensure the import of
github.com/bytedance/gopkg/util/logger conforms to our dependency policy (either
keep it if allowed for adaptor.go only or replace with the project's approved
logger package and update call sites), change convertProgress so it does not
silently return 0 on parse failures by returning an (int, error) or logging the
parse failure and propagating the error to callers, and update the
error-attachment check from using && to || (i.e., if resp.Code != 0 ||
resp.Message != "") so message-only errors are not missed; adjust callers to
handle the new convertProgress error return or rely on the logged error.

Comment on lines +126 to +128
if _, ok := form.Value["model"]; !ok {
return createTaskError(fmt.Errorf("model field is required"), "missing_model", http.StatusBadRequest, true)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Validate that model value is non-empty.

The code checks if the "model" field exists in the form but doesn't validate that it's not an empty string, which could lead to downstream validation issues.

Apply this diff:

-		if _, ok := form.Value["model"]; !ok {
+		models, hasModel := form.Value["model"]
+		if !hasModel || len(models) == 0 || strings.TrimSpace(models[0]) == "" {
 			return createTaskError(fmt.Errorf("model field is required"), "missing_model", http.StatusBadRequest, true)
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if _, ok := form.Value["model"]; !ok {
return createTaskError(fmt.Errorf("model field is required"), "missing_model", http.StatusBadRequest, true)
}
models, hasModel := form.Value["model"]
if !hasModel || len(models) == 0 || strings.TrimSpace(models[0]) == "" {
return createTaskError(fmt.Errorf("model field is required"), "missing_model", http.StatusBadRequest, true)
}
🤖 Prompt for AI Agents
In relay/common/relay_utils.go around lines 126 to 128, the code only checks
that the "model" key exists but not that its value is non-empty; update the
validation to ensure form.Value["model"] has at least one entry and that the
first entry is not an empty or whitespace-only string (e.g., check
len(valueSlice) > 0 and strings.TrimSpace(valueSlice[0]) != ""), and return the
same createTaskError("model field is required", "missing_model",
http.StatusBadRequest, true) when the value is empty; import strings if needed.

Comment thread relay/relay_task.go
Comment on lines +371 to +374
adaptor := GetTaskAdaptor(originTask.Platform)
if adaptor == nil {
taskResp = service.TaskErrorWrapperLocal(fmt.Errorf("invalid channel id: %d", originTask.ChannelId), "invalid_channel_id", http.StatusBadRequest)
return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Improve error message accuracy.

The error code "invalid_channel_id" on Line 373 is misleading when the actual issue is that no adaptor exists for the platform. Consider using a more descriptive error code.

Apply this diff:

 		adaptor := GetTaskAdaptor(originTask.Platform)
 		if adaptor == nil {
-			taskResp = service.TaskErrorWrapperLocal(fmt.Errorf("invalid channel id: %d", originTask.ChannelId), "invalid_channel_id", http.StatusBadRequest)
+			taskResp = service.TaskErrorWrapperLocal(fmt.Errorf("no adaptor found for platform: %s", originTask.Platform), "adaptor_not_found", http.StatusBadRequest)
 			return
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
adaptor := GetTaskAdaptor(originTask.Platform)
if adaptor == nil {
taskResp = service.TaskErrorWrapperLocal(fmt.Errorf("invalid channel id: %d", originTask.ChannelId), "invalid_channel_id", http.StatusBadRequest)
return
adaptor := GetTaskAdaptor(originTask.Platform)
if adaptor == nil {
taskResp = service.TaskErrorWrapperLocal(fmt.Errorf("no adaptor found for platform: %s", originTask.Platform), "adaptor_not_found", http.StatusBadRequest)
return
}
🤖 Prompt for AI Agents
In relay/relay_task.go around lines 371 to 374, the error returned when
GetTaskAdaptor(originTask.Platform) is nil currently reports "invalid channel
id" and uses error code "invalid_channel_id", which is misleading; change the
error to reference the missing/unsupported platform (e.g., fmt.Errorf("no
adaptor for platform: %v", originTask.Platform)) and use a descriptive error
code like "unsupported_platform" (retain http.StatusBadRequest), so the log and
response accurately reflect that no adaptor exists for the given platform.

@Calcium-Ion Calcium-Ion merged commit 1031f1d into QuantumNous:main Oct 11, 2025
1 check passed
ennnnny pushed a commit to ennnnny/new-api that referenced this pull request Mar 17, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Mar 28, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Apr 22, 2026
11 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants