Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 5 additions & 21 deletions relay/channel/task/jimeng/adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/pkg/errors"

"one-api/common"
"one-api/constant"
"one-api/dto"
"one-api/relay/channel"
Expand Down Expand Up @@ -89,22 +88,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) {
// ValidateRequestAndSetAction parses body, validates fields and sets default action.
func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) {
// Accept only POST /v1/video/generations as "generate" action.
action := constant.TaskActionGenerate
info.Action = action

req := relaycommon.TaskSubmitReq{}
if err := common.UnmarshalBodyReusable(c, &req); err != nil {
taskErr = service.TaskErrorWrapperLocal(err, "invalid_request", http.StatusBadRequest)
return
}
if strings.TrimSpace(req.Prompt) == "" {
taskErr = service.TaskErrorWrapperLocal(fmt.Errorf("prompt is required"), "invalid_request", http.StatusBadRequest)
return
}

// Store into context for later usage
c.Set("task_request", req)
return nil
return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionGenerate)
}

// BuildRequestURL constructs the upstream URL.
Expand Down Expand Up @@ -334,11 +318,11 @@ func (a *TaskAdaptor) convertToRequestPayload(req *relaycommon.TaskSubmitReq) (*
}

// Handle one-of image_urls or binary_data_base64
if req.Image != "" {
if strings.HasPrefix(req.Image, "http") {
r.ImageUrls = []string{req.Image}
if req.HasImage() {
if strings.HasPrefix(req.Images[0], "http") {
r.ImageUrls = req.Images
} else {
r.BinaryDataBase64 = []string{req.Image}
r.BinaryDataBase64 = req.Images
}
}
metadata := req.Metadata
Expand Down
34 changes: 4 additions & 30 deletions relay/channel/task/kling/adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/golang-jwt/jwt"
"github.com/pkg/errors"

"one-api/common"
"one-api/constant"
"one-api/dto"
"one-api/relay/channel"
Expand All @@ -28,16 +27,6 @@ import (
// Request / Response structures
// ============================

type SubmitReq struct {
Prompt string `json:"prompt"`
Model string `json:"model,omitempty"`
Mode string `json:"mode,omitempty"`
Image string `json:"image,omitempty"`
Size string `json:"size,omitempty"`
Duration int `json:"duration,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}

type TrajectoryPoint struct {
X int `json:"x"`
Y int `json:"y"`
Expand Down Expand Up @@ -121,23 +110,8 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) {

// ValidateRequestAndSetAction parses body, validates fields and sets default action.
func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) {
// Accept only POST /v1/video/generations as "generate" action.
action := constant.TaskActionGenerate
info.Action = action

var req SubmitReq
if err := common.UnmarshalBodyReusable(c, &req); err != nil {
taskErr = service.TaskErrorWrapperLocal(err, "invalid_request", http.StatusBadRequest)
return
}
if strings.TrimSpace(req.Prompt) == "" {
taskErr = service.TaskErrorWrapperLocal(fmt.Errorf("prompt is required"), "invalid_request", http.StatusBadRequest)
return
}

// Store into context for later usage
c.Set("task_request", req)
return nil
// Use the standard validation method for TaskSubmitReq
return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionGenerate)
}

// BuildRequestURL constructs the upstream URL.
Expand Down Expand Up @@ -166,7 +140,7 @@ func (a *TaskAdaptor) BuildRequestBody(c *gin.Context, info *relaycommon.RelayIn
if !exists {
return nil, fmt.Errorf("request not found in context")
}
req := v.(SubmitReq)
req := v.(relaycommon.TaskSubmitReq)

body, err := a.convertToRequestPayload(&req)
if err != nil {
Expand Down Expand Up @@ -255,7 +229,7 @@ func (a *TaskAdaptor) GetChannelName() string {
// helpers
// ============================

func (a *TaskAdaptor) convertToRequestPayload(req *SubmitReq) (*requestPayload, error) {
func (a *TaskAdaptor) convertToRequestPayload(req *relaycommon.TaskSubmitReq) (*requestPayload, error) {
r := requestPayload{
Prompt: req.Prompt,
Image: req.Image,
Expand Down
33 changes: 4 additions & 29 deletions relay/channel/task/vidu/adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,6 @@ import (
// Request / Response structures
// ============================

type SubmitReq struct {
Prompt string `json:"prompt"`
Model string `json:"model,omitempty"`
Mode string `json:"mode,omitempty"`
Image string `json:"image,omitempty"`
Size string `json:"size,omitempty"`
Duration int `json:"duration,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}

type requestPayload struct {
Model string `json:"model"`
Images []string `json:"images"`
Expand Down Expand Up @@ -90,31 +80,16 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) {
}

func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) *dto.TaskError {
var req SubmitReq
if err := c.ShouldBindJSON(&req); err != nil {
return service.TaskErrorWrapper(err, "invalid_request_body", http.StatusBadRequest)
}

if req.Prompt == "" {
return service.TaskErrorWrapperLocal(fmt.Errorf("prompt is required"), "missing_prompt", http.StatusBadRequest)
}

if req.Image != "" {
info.Action = constant.TaskActionGenerate
} else {
info.Action = constant.TaskActionTextGenerate
}

c.Set("task_request", req)
return nil
// Use the unified validation method for TaskSubmitReq with image-based action determination
return relaycommon.ValidateTaskRequestWithImageBinding(c, info)
}
Comment on lines +83 to 85

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.

💡 Verification agent

🧩 Analysis chain

Validator path can misclassify image requests when only image is provided.

ValidateTaskRequestWithImageBinding doesn’t merge image -> images, so action may be set to text-generation incorrectly. Either switch to ValidateBasicTaskRequest or fix the validator (see relay/common/relay_utils.go). If you keep this path, ensure convert uses req.Images.


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "CWD: $(pwd)"
echo "----- locate validators -----"
rg -n --hidden -S "ValidateTaskRequestWithImageBinding|ValidateBasicTaskRequest|ValidateTaskRequest" || true

echo
echo "----- locate TaskSubmitReq and image fields -----"
rg -n --hidden -S "type\s+TaskSubmitReq\b|TaskSubmitReq\b|req\.Image\b|req\.Images\b|\bImage\b|\bImages\b" || true

echo
echo "----- show reported file if present -----"
if [ -f "relay/channel/task/vidu/adaptor.go" ]; then
  echo "---- relay/channel/task/vidu/adaptor.go ----"
  sed -n '1,260p' relay/channel/task/vidu/adaptor.go
else
  echo "relay/channel/task/vidu/adaptor.go not found; listing relay/channel/task"
  ls -la relay/channel || true
  ls -la relay/channel/task || true
fi

echo
echo "----- search for relay/common/relay_utils.go -----"
rg -n --hidden -S "relay/common/relay_utils.go|relay_utils.go" || true
if [ -f "relay/common/relay_utils.go" ]; then
  echo "---- relay/common/relay_utils.go ----"
  sed -n '1,400p' relay/common/relay_utils.go
fi

echo
echo "----- finished -----"

Length of output: 17116


Validator misclassifies single-image requests — merge image->images or switch validator

ValidateTaskRequestWithImageBinding binds into TaskSubmitReq but does not copy TaskSubmitReq.Image -> TaskSubmitReq.Images (ValidateBasicTaskRequest does), so HasImage() can be false when only "image" is provided and info.Action becomes text-generation — causing vidu to pick /text2video while the body contains images.

  • Fix (recommended): in relay/common/relay_utils.go, inside ValidateTaskRequestWithImageBinding, add the same compatibility copy before calling ValidateTaskRequestWithImage:
    if len(req.Images) == 0 && strings.TrimSpace(req.Image) != "" {
    req.Images = []string{req.Image}
    }
  • Alternative: change relay/channel/task/vidu/adaptor.go (ValidateRequestAndSetAction) to use ValidateBasicTaskRequest and handle action explicitly.

Files: relay/common/relay_utils.go (ValidateTaskRequestWithImageBinding), relay/channel/task/vidu/adaptor.go (ValidateRequestAndSetAction).


func (a *TaskAdaptor) BuildRequestBody(c *gin.Context, _ *relaycommon.RelayInfo) (io.Reader, error) {
v, exists := c.Get("task_request")
if !exists {
return nil, fmt.Errorf("request not found in context")
}
req := v.(SubmitReq)
req := v.(relaycommon.TaskSubmitReq)

body, err := a.convertToRequestPayload(&req)
if err != nil {
Expand Down Expand Up @@ -211,7 +186,7 @@ func (a *TaskAdaptor) GetChannelName() string {
// helpers
// ============================

func (a *TaskAdaptor) convertToRequestPayload(req *SubmitReq) (*requestPayload, error) {
func (a *TaskAdaptor) convertToRequestPayload(req *relaycommon.TaskSubmitReq) (*requestPayload, error) {
var images []string
if req.Image != "" {
images = []string{req.Image}
Expand Down
9 changes: 9 additions & 0 deletions relay/common/relay_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,11 +481,20 @@ type TaskSubmitReq struct {
Model string `json:"model,omitempty"`
Mode string `json:"mode,omitempty"`
Image string `json:"image,omitempty"`
Images []string `json:"images,omitempty"`
Size string `json:"size,omitempty"`
Duration int `json:"duration,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}

func (t TaskSubmitReq) GetPrompt() string {
return t.Prompt
}

func (t TaskSubmitReq) HasImage() bool {
return len(t.Images) > 0
}
Comment on lines +494 to +496

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.

💡 Verification agent

🧩 Analysis chain

HasImage() misses legacy image path used by some validators.

ValidateTaskRequestWithImageBinding does not merge image -> images, so HasImage() can return false when only image is provided. Either (a) make HasImage() check both fields, or (b) fix the validator (preferred, see relay/common/relay_utils.go comment). If you prefer fixing here, apply:

-func (t TaskSubmitReq) HasImage() bool {
-  return len(t.Images) > 0
-}
+func (t TaskSubmitReq) HasImage() bool {
+  if len(t.Images) > 0 {
+    return true
+  }
+  return strings.TrimSpace(t.Image) != ""
+}

🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "---- locate TaskSubmitReq definition ----"
rg -n --hidden --no-ignore-vcs "type\s+TaskSubmitReq\b" -S || true

echo "---- show files defining TaskSubmitReq ----"
rg -n --hidden --no-ignore-vcs "type\s+TaskSubmitReq\b" -S | cut -d: -f1 | uniq | while read -r f; do
  echo "=== $f ==="
  sed -n '1,240p' "$f" || true
done

echo "---- relay/common/relay_info.go (lines 460-520) ----"
if [ -f relay/common/relay_info.go ]; then
  sed -n '460,520p' relay/common/relay_info.go || true
else
  rg -n --hidden --no-ignore-vcs "relay/common/relay_info.go" -S || true
fi

echo "---- HasImage implementations ----"
rg -n --hidden --no-ignore-vcs "func\s+\(t\s+\*?TaskSubmitReq\)\s+HasImage\s*\(" -S || true

echo "---- ValidateTaskRequestWithImageBinding occurrences ----"
rg -n --hidden --no-ignore-vcs "ValidateTaskRequestWithImageBinding" -S || true

echo "---- relay/common/relay_utils.go and image mentions ----"
if [ -f relay/common/relay_utils.go ]; then
  sed -n '1,240p' relay/common/relay_utils.go || true
else
  rg -n --hidden --no-ignore-vcs "relay_utils\.go" -S || true
fi

echo "---- search for Image / Images fields ----"
rg -n --hidden --no-ignore-vcs "\bImage(s)?\b" -S || true

Length of output: 17505


Merge legacy image into images in ValidateTaskRequestWithImageBinding (preferred)

ValidateTaskRequestWithImageBinding uses c.ShouldBindJSON(&req) but does not copy req.Image → req.Images; TaskSubmitReq.HasImage only checks Images, so requests with only "image" are treated as no-image.

Action: in relay/common/relay_utils.go, inside ValidateTaskRequestWithImageBinding (before calling ValidateTaskRequestWithImage), add the same compatibility block used in ValidateBasicTaskRequest: if len(req.Images) == 0 && strings.TrimSpace(req.Image) != "" { req.Images = []string{req.Image} }.
Alternative: change TaskSubmitReq.HasImage to check both fields (e.g. return len(t.Images) > 0 || strings.TrimSpace(t.Image) != "").

🤖 Prompt for AI Agents
In relay/common/relay_utils.go in the ValidateTaskRequestWithImageBinding
function (insert before the call to ValidateTaskRequestWithImage), merge legacy
req.Image into req.Images by adding the compatibility block: if len(req.Images)
== 0 && strings.TrimSpace(req.Image) != "" { req.Images = []string{req.Image} };
this ensures requests using the old "image" field are treated as having images;
alternatively, you can modify TaskSubmitReq.HasImage to check both Images and
Image, but prefer adding the compatibility block in
ValidateTaskRequestWithImageBinding.


type TaskInfo struct {
Code int `json:"code"`
TaskID string `json:"task_id"`
Expand Down
80 changes: 80 additions & 0 deletions relay/common/relay_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@ package common

import (
"fmt"
"net/http"
"one-api/common"
"one-api/constant"
"one-api/dto"
"strings"

"github.com/gin-gonic/gin"
)

type HasPrompt interface {
GetPrompt() string
}

type HasImage interface {
HasImage() bool
}

func GetFullRequestURL(baseURL string, requestURL string, channelType int) string {
fullRequestURL := fmt.Sprintf("%s%s", baseURL, requestURL)

Expand All @@ -30,3 +41,72 @@ func GetAPIVersion(c *gin.Context) string {
}
return apiVersion
}

func createTaskError(err error, code string, statusCode int, localError bool) *dto.TaskError {
return &dto.TaskError{
Code: code,
Message: err.Error(),
StatusCode: statusCode,
LocalError: localError,
Error: err,
}
}

func storeTaskRequest(c *gin.Context, info *RelayInfo, action string, requestObj interface{}) {
info.Action = action
c.Set("task_request", requestObj)
}

func validatePrompt(prompt string) *dto.TaskError {
if strings.TrimSpace(prompt) == "" {
return createTaskError(fmt.Errorf("prompt is required"), "invalid_request", http.StatusBadRequest, true)
}
return nil
}

func ValidateBasicTaskRequest(c *gin.Context, info *RelayInfo, action string) *dto.TaskError {
var req TaskSubmitReq
if err := common.UnmarshalBodyReusable(c, &req); err != nil {
return createTaskError(err, "invalid_request", http.StatusBadRequest, true)
}

if taskErr := validatePrompt(req.Prompt); taskErr != nil {
return taskErr
}

if len(req.Images) == 0 && strings.TrimSpace(req.Image) != "" {
// 兼容单图上传
req.Images = []string{req.Image}
}

storeTaskRequest(c, info, action, req)
return nil
}

func ValidateTaskRequestWithImage(c *gin.Context, info *RelayInfo, requestObj interface{}) *dto.TaskError {
hasPrompt, ok := requestObj.(HasPrompt)
if !ok {
return createTaskError(fmt.Errorf("request must have prompt"), "invalid_request", http.StatusBadRequest, true)
}

if taskErr := validatePrompt(hasPrompt.GetPrompt()); taskErr != nil {
return taskErr
}

action := constant.TaskActionTextGenerate
if hasImage, ok := requestObj.(HasImage); ok && hasImage.HasImage() {
action = constant.TaskActionGenerate
}

storeTaskRequest(c, info, action, requestObj)
return nil
}

func ValidateTaskRequestWithImageBinding(c *gin.Context, info *RelayInfo) *dto.TaskError {
var req TaskSubmitReq
if err := c.ShouldBindJSON(&req); err != nil {
return createTaskError(err, "invalid_request_body", http.StatusBadRequest, false)
}

return ValidateTaskRequestWithImage(c, info, req)
}
Comment on lines +105 to +112

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.

💡 Verification agent

🧩 Analysis chain

Fix ValidateTaskRequestWithImageBinding to merge imageimages and reuse reusable-body unmarshal.

Without this, adaptors using this path (e.g., vidu) misclassify requests and lose images.

-func ValidateTaskRequestWithImageBinding(c *gin.Context, info *RelayInfo) *dto.TaskError {
-  var req TaskSubmitReq
-  if err := c.ShouldBindJSON(&req); err != nil {
-    return createTaskError(err, "invalid_request_body", http.StatusBadRequest, false)
-  }
-
-  return ValidateTaskRequestWithImage(c, info, req)
-}
+func ValidateTaskRequestWithImageBinding(c *gin.Context, info *RelayInfo) *dto.TaskError {
+  var req TaskSubmitReq
+  if err := common.UnmarshalBodyReusable(c, &req); err != nil {
+    return createTaskError(err, "invalid_request", http.StatusBadRequest, true)
+  }
+  if len(req.Images) == 0 && strings.TrimSpace(req.Image) != "" {
+    req.Images = []string{req.Image}
+  }
+  return ValidateTaskRequestWithImage(c, info, req)
+}

🏁 Script executed:

#!/bin/bash
set -euo pipefail
printf "=== rg: ValidateTaskRequestWithImageBinding ===\n"
rg -n --hidden --no-ignore "ValidateTaskRequestWithImageBinding" -S || true

printf "\n=== show: relay/common/relay_utils.go (first 240 lines) ===\n"
if [ -f relay/common/relay_utils.go ]; then
  sed -n '1,240p' relay/common/relay_utils.go || true
else
  printf "relay/common/relay_utils.go not found\n"
fi

printf "\n=== rg: TaskSubmitReq definition ===\n"
rg -n --hidden --no-ignore "type\\s+TaskSubmitReq" -S || true

printf "\n=== rg: occurrences of TaskSubmitReq ===\n"
rg -n --hidden --no-ignore "TaskSubmitReq" -S || true

printf "\n=== rg: UnmarshalBodyReusable ===\n"
rg -n --hidden --no-ignore "UnmarshalBodyReusable" -S || true

printf "\n=== rg: createTaskError definition ===\n"
rg -n --hidden --no-ignore "func\\s+createTaskError" -S || true

printf "\n=== rg: ValidateTaskRequestWithImage definition ===\n"
rg -n --hidden --no-ignore "func\\s+ValidateTaskRequestWithImage" -S || true

Length of output: 7308


Fix ValidateTaskRequestWithImageBinding: use UnmarshalBodyReusable and merge single imageimages.

Mirror ValidateBasicTaskRequest (use common.UnmarshalBodyReusable and preserve single-image compatibility) — current ShouldBindJSON loses images for adaptors like vidu.

Location: relay/common/relay_utils.go:105-112

-func ValidateTaskRequestWithImageBinding(c *gin.Context, info *RelayInfo) *dto.TaskError {
-  var req TaskSubmitReq
-  if err := c.ShouldBindJSON(&req); err != nil {
-    return createTaskError(err, "invalid_request_body", http.StatusBadRequest, false)
-  }
-
-  return ValidateTaskRequestWithImage(c, info, req)
-}
+func ValidateTaskRequestWithImageBinding(c *gin.Context, info *RelayInfo) *dto.TaskError {
+  var req TaskSubmitReq
+  if err := common.UnmarshalBodyReusable(c, &req); err != nil {
+    return createTaskError(err, "invalid_request", http.StatusBadRequest, true)
+  }
+  if len(req.Images) == 0 && strings.TrimSpace(req.Image) != "" {
+    req.Images = []string{req.Image}
+  }
+  return ValidateTaskRequestWithImage(c, info, req)
+}
📝 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
func ValidateTaskRequestWithImageBinding(c *gin.Context, info *RelayInfo) *dto.TaskError {
var req TaskSubmitReq
if err := c.ShouldBindJSON(&req); err != nil {
return createTaskError(err, "invalid_request_body", http.StatusBadRequest, false)
}
return ValidateTaskRequestWithImage(c, info, req)
}
func ValidateTaskRequestWithImageBinding(c *gin.Context, info *RelayInfo) *dto.TaskError {
var req TaskSubmitReq
if err := common.UnmarshalBodyReusable(c, &req); err != nil {
return createTaskError(err, "invalid_request", http.StatusBadRequest, true)
}
if len(req.Images) == 0 && strings.TrimSpace(req.Image) != "" {
req.Images = []string{req.Image}
}
return ValidateTaskRequestWithImage(c, info, req)
}
🤖 Prompt for AI Agents
In relay/common/relay_utils.go around lines 105-112, the function currently uses
c.ShouldBindJSON which drops adaptor-provided images; replace the JSON bind with
common.UnmarshalBodyReusable to parse the request into req, then preserve
single-image compatibility by checking if req.Images is empty and req.Image (or
the single-image field used in DTO) is set — if so, populate req.Images with
that single value; finally call ValidateTaskRequestWithImage(c, info, req) as
before. Ensure error handling mirrors ValidateBasicTaskRequest (return
createTaskError on unmarshal failure) and keep the same return type.