Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
92c79548
authored
Oct 11, 2025
by
Calcium-Ion
Committed by
GitHub
Oct 11, 2025
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2004 from feitianbubu/pr/openai-sdk-kling
支持可灵使用openai sdk生成视频
parents
a92afd7a
06ae3efa
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
140 additions
and
13 deletions
+140
-13
middleware/distributor.go
+8
-0
relay/channel/adapter.go
+5
-0
relay/channel/task/kling/adaptor.go
+49
-8
relay/channel/task/sora/adaptor.go
+9
-0
relay/common/relay_info.go
+21
-0
relay/common/relay_utils.go
+32
-4
relay/relay_task.go
+16
-1
No files found.
middleware/distributor.go
View file @
92c79548
...
...
@@ -174,6 +174,8 @@ func getModelRequest(c *gin.Context) (*ModelRequest, bool, error) {
relayMode
:=
relayconstant
.
RelayModeUnknown
if
c
.
Request
.
Method
==
http
.
MethodPost
{
relayMode
=
relayconstant
.
RelayModeVideoSubmit
contentType
:=
c
.
Request
.
Header
.
Get
(
"Content-Type"
)
if
strings
.
HasPrefix
(
contentType
,
"multipart/form-data"
)
{
form
,
err
:=
common
.
ParseMultipartFormReusable
(
c
)
if
err
!=
nil
{
return
nil
,
false
,
errors
.
New
(
"无效的video请求, "
+
err
.
Error
())
...
...
@@ -184,6 +186,12 @@ func getModelRequest(c *gin.Context) (*ModelRequest, bool, error) {
modelRequest
.
Model
=
values
[
0
]
}
}
}
else
if
strings
.
HasPrefix
(
contentType
,
"application/json"
)
{
err
=
common
.
UnmarshalBodyReusable
(
c
,
&
modelRequest
)
if
err
!=
nil
{
return
nil
,
false
,
errors
.
New
(
"无效的video请求, "
+
err
.
Error
())
}
}
}
else
if
c
.
Request
.
Method
==
http
.
MethodGet
{
relayMode
=
relayconstant
.
RelayModeVideoFetchByID
shouldSelectChannel
=
false
...
...
relay/channel/adapter.go
View file @
92c79548
...
...
@@ -4,6 +4,7 @@ import (
"io"
"net/http"
"one-api/dto"
"one-api/model"
relaycommon
"one-api/relay/common"
"one-api/types"
...
...
@@ -49,3 +50,7 @@ type TaskAdaptor interface {
ParseTaskResult
(
respBody
[]
byte
)
(
*
relaycommon
.
TaskInfo
,
error
)
}
type
OpenAIVideoConverter
interface
{
ConvertToOpenAIVideo
(
originTask
*
model
.
Task
)
(
*
relaycommon
.
OpenAIVideo
,
error
)
}
relay/channel/task/kling/adaptor.go
View file @
92c79548
...
...
@@ -7,9 +7,11 @@ import (
"io"
"net/http"
"one-api/model"
"strconv"
"strings"
"time"
"github.com/bytedance/gopkg/util/logger"
"github.com/samber/lo"
"github.com/gin-gonic/gin"
...
...
@@ -303,14 +305,6 @@ func (a *TaskAdaptor) createJWTToken() (string, error) {
return
a
.
createJWTTokenWithKey
(
a
.
apiKey
)
}
//func (a *TaskAdaptor) createJWTTokenWithKey(apiKey string) (string, error) {
// parts := strings.Split(apiKey, "|")
// if len(parts) != 2 {
// return "", fmt.Errorf("invalid API key format, expected 'access_key,secret_key'")
// }
// return a.createJWTTokenWithKey(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]))
//}
func
(
a
*
TaskAdaptor
)
createJWTTokenWithKey
(
apiKey
string
)
(
string
,
error
)
{
if
isNewAPIRelay
(
apiKey
)
{
return
apiKey
,
nil
// new api relay
...
...
@@ -369,3 +363,50 @@ func (a *TaskAdaptor) ParseTaskResult(respBody []byte) (*relaycommon.TaskInfo, e
func
isNewAPIRelay
(
apiKey
string
)
bool
{
return
strings
.
HasPrefix
(
apiKey
,
"sk-"
)
}
func
(
a
*
TaskAdaptor
)
ConvertToOpenAIVideo
(
originTask
*
model
.
Task
)
(
*
relaycommon
.
OpenAIVideo
,
error
)
{
var
klingResp
responsePayload
if
err
:=
json
.
Unmarshal
(
originTask
.
Data
,
&
klingResp
);
err
!=
nil
{
return
nil
,
errors
.
Wrap
(
err
,
"unmarshal kling task data failed"
)
}
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
p
}
openAIVideo
:=
&
relaycommon
.
OpenAIVideo
{
ID
:
klingResp
.
Data
.
TaskId
,
Object
:
"video"
,
//Model: "kling-v1", //todo save model
Status
:
string
(
originTask
.
Status
),
CreatedAt
:
klingResp
.
Data
.
CreatedAt
,
CompletedAt
:
klingResp
.
Data
.
UpdatedAt
,
Metadata
:
make
(
map
[
string
]
any
),
Progress
:
convertProgress
(
originTask
.
Progress
),
}
// 处理视频 URL
if
len
(
klingResp
.
Data
.
TaskResult
.
Videos
)
>
0
{
video
:=
klingResp
.
Data
.
TaskResult
.
Videos
[
0
]
if
video
.
Url
!=
""
{
openAIVideo
.
Metadata
[
"url"
]
=
video
.
Url
}
if
video
.
Duration
!=
""
{
openAIVideo
.
Seconds
=
video
.
Duration
}
}
if
klingResp
.
Code
!=
0
&&
klingResp
.
Message
!=
""
{
openAIVideo
.
Error
=
&
relaycommon
.
OpenAIVideoError
{
Message
:
klingResp
.
Message
,
Code
:
fmt
.
Sprintf
(
"%d"
,
klingResp
.
Code
),
}
}
return
openAIVideo
,
nil
}
relay/channel/task/sora/adaptor.go
View file @
92c79548
...
...
@@ -184,3 +184,12 @@ func (a *TaskAdaptor) ParseTaskResult(respBody []byte) (*relaycommon.TaskInfo, e
return
&
taskResult
,
nil
}
func
(
a
*
TaskAdaptor
)
ConvertToOpenAIVideo
(
task
*
model
.
Task
)
(
*
relaycommon
.
OpenAIVideo
,
error
)
{
openAIVideo
:=
&
relaycommon
.
OpenAIVideo
{}
err
:=
json
.
Unmarshal
(
task
.
Data
,
openAIVideo
)
if
err
!=
nil
{
return
nil
,
errors
.
Wrap
(
err
,
"unmarshal to OpenAIVideo failed"
)
}
return
openAIVideo
,
nil
}
relay/common/relay_info.go
View file @
92c79548
...
...
@@ -550,3 +550,24 @@ func RemoveDisabledFields(jsonData []byte, channelOtherSettings dto.ChannelOther
}
return
jsonDataAfter
,
nil
}
type
OpenAIVideo
struct
{
ID
string
`json:"id"`
TaskID
string
`json:"task_id,omitempty"`
//兼容旧接口 待废弃
Object
string
`json:"object"`
Model
string
`json:"model"`
Status
string
`json:"status"`
Progress
int
`json:"progress"`
CreatedAt
int64
`json:"created_at"`
CompletedAt
int64
`json:"completed_at,omitempty"`
ExpiresAt
int64
`json:"expires_at,omitempty"`
Seconds
string
`json:"seconds,omitempty"`
Size
string
`json:"size,omitempty"`
RemixedFromVideoID
string
`json:"remixed_from_video_id,omitempty"`
Error
*
OpenAIVideoError
`json:"error,omitempty"`
Metadata
map
[
string
]
any
`json:"metadata,omitempty"`
}
type
OpenAIVideoError
struct
{
Message
string
`json:"message"`
Code
string
`json:"code"`
}
relay/common/relay_utils.go
View file @
92c79548
...
...
@@ -106,6 +106,11 @@ func validateMultipartTaskRequest(c *gin.Context, info *RelayInfo, action string
}
func
ValidateMultipartDirect
(
c
*
gin
.
Context
,
info
*
RelayInfo
)
*
dto
.
TaskError
{
contentType
:=
c
.
GetHeader
(
"Content-Type"
)
var
prompt
string
var
hasInputReference
bool
if
strings
.
HasPrefix
(
contentType
,
"multipart/form-data"
)
{
form
,
err
:=
common
.
ParseMultipartFormReusable
(
c
)
if
err
!=
nil
{
return
createTaskError
(
err
,
"invalid_multipart_form"
,
http
.
StatusBadRequest
,
true
)
...
...
@@ -116,15 +121,38 @@ func ValidateMultipartDirect(c *gin.Context, info *RelayInfo) *dto.TaskError {
if
!
ok
||
len
(
prompts
)
==
0
{
return
createTaskError
(
fmt
.
Errorf
(
"prompt field is required"
),
"missing_prompt"
,
http
.
StatusBadRequest
,
true
)
}
if
taskErr
:=
validatePrompt
(
prompts
[
0
]);
taskErr
!=
nil
{
return
taskErr
}
prompt
=
prompts
[
0
]
if
_
,
ok
:=
form
.
Value
[
"model"
];
!
ok
{
return
createTaskError
(
fmt
.
Errorf
(
"model field is required"
),
"missing_model"
,
http
.
StatusBadRequest
,
true
)
}
action
:=
constant
.
TaskActionTextGenerate
if
_
,
ok
:=
form
.
File
[
"input_reference"
];
ok
{
hasInputReference
=
true
}
}
else
{
var
req
TaskSubmitReq
if
err
:=
common
.
UnmarshalBodyReusable
(
c
,
&
req
);
err
!=
nil
{
return
createTaskError
(
err
,
"invalid_json"
,
http
.
StatusBadRequest
,
true
)
}
prompt
=
req
.
Prompt
if
strings
.
TrimSpace
(
req
.
Model
)
==
""
{
return
createTaskError
(
fmt
.
Errorf
(
"model field is required"
),
"missing_model"
,
http
.
StatusBadRequest
,
true
)
}
if
req
.
HasImage
()
{
hasInputReference
=
true
}
}
if
taskErr
:=
validatePrompt
(
prompt
);
taskErr
!=
nil
{
return
taskErr
}
action
:=
constant
.
TaskActionTextGenerate
if
hasInputReference
{
action
=
constant
.
TaskActionGenerate
}
info
.
Action
=
action
...
...
relay/relay_task.go
View file @
92c79548
...
...
@@ -11,6 +11,7 @@ import (
"one-api/constant"
"one-api/dto"
"one-api/model"
"one-api/relay/channel"
relaycommon
"one-api/relay/common"
relayconstant
"one-api/relay/constant"
"one-api/service"
...
...
@@ -367,7 +368,21 @@ func videoFetchByIDRespBodyBuilder(c *gin.Context) (respBody []byte, taskResp *d
}
if
strings
.
HasPrefix
(
c
.
Request
.
RequestURI
,
"/v1/videos/"
)
{
respBody
=
originTask
.
Data
adaptor
:=
GetTaskAdaptor
(
originTask
.
Platform
)
if
adaptor
==
nil
{
taskResp
=
service
.
TaskErrorWrapperLocal
(
fmt
.
Errorf
(
"invalid channel id: %d"
,
originTask
.
ChannelId
),
"invalid_channel_id"
,
http
.
StatusBadRequest
)
return
}
if
converter
,
ok
:=
adaptor
.
(
channel
.
OpenAIVideoConverter
);
ok
{
openAIVideo
,
err
:=
converter
.
ConvertToOpenAIVideo
(
originTask
)
if
err
!=
nil
{
taskResp
=
service
.
TaskErrorWrapper
(
err
,
"convert_to_openai_video_failed"
,
http
.
StatusInternalServerError
)
return
}
respBody
,
_
=
json
.
Marshal
(
openAIVideo
)
return
}
taskResp
=
service
.
TaskErrorWrapperLocal
(
errors
.
New
(
fmt
.
Sprintf
(
"not_implemented:%s"
,
originTask
.
Platform
)),
"not_implemented"
,
http
.
StatusNotImplemented
)
return
}
respBody
,
err
=
json
.
Marshal
(
dto
.
TaskResponse
[
any
]{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment