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
4307065c
authored
Jun 21, 2025
by
CaIon
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'main' into alpha
parents
d09292bd
23cd90a8
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
165 additions
and
2 deletions
+165
-2
controller/playground.go
+9
-0
dto/dalle.go
+1
-0
relay/channel/volcengine/adaptor.go
+148
-2
relay/image_handler.go
+7
-0
No files found.
controller/playground.go
View file @
4307065c
...
@@ -66,5 +66,14 @@ func Playground(c *gin.Context) {
...
@@ -66,5 +66,14 @@ func Playground(c *gin.Context) {
}
}
middleware
.
SetupContextForSelectedChannel
(
c
,
channel
,
playgroundRequest
.
Model
)
middleware
.
SetupContextForSelectedChannel
(
c
,
channel
,
playgroundRequest
.
Model
)
c
.
Set
(
constant
.
ContextKeyRequestStartTime
,
time
.
Now
())
c
.
Set
(
constant
.
ContextKeyRequestStartTime
,
time
.
Now
())
// Write user context to ensure acceptUnsetRatio is available
userId
:=
c
.
GetInt
(
"id"
)
userCache
,
err
:=
model
.
GetUserCache
(
userId
)
if
err
!=
nil
{
openaiErr
=
service
.
OpenAIErrorWrapperLocal
(
err
,
"get_user_cache_failed"
,
http
.
StatusInternalServerError
)
return
}
userCache
.
WriteContext
(
c
)
Relay
(
c
)
Relay
(
c
)
}
}
dto/dalle.go
View file @
4307065c
...
@@ -15,6 +15,7 @@ type ImageRequest struct {
...
@@ -15,6 +15,7 @@ type ImageRequest struct {
Background
string
`json:"background,omitempty"`
Background
string
`json:"background,omitempty"`
Moderation
string
`json:"moderation,omitempty"`
Moderation
string
`json:"moderation,omitempty"`
OutputFormat
string
`json:"output_format,omitempty"`
OutputFormat
string
`json:"output_format,omitempty"`
Watermark
*
bool
`json:"watermark,omitempty"`
}
}
type
ImageResponse
struct
{
type
ImageResponse
struct
{
...
...
relay/channel/volcengine/adaptor.go
View file @
4307065c
package
volcengine
package
volcengine
import
(
import
(
"bytes"
"errors"
"errors"
"fmt"
"fmt"
"io"
"io"
"mime/multipart"
"net/http"
"net/http"
"net/textproto"
"one-api/dto"
"one-api/dto"
"one-api/relay/channel"
"one-api/relay/channel"
"one-api/relay/channel/openai"
"one-api/relay/channel/openai"
relaycommon
"one-api/relay/common"
relaycommon
"one-api/relay/common"
"one-api/relay/constant"
"one-api/relay/constant"
"path/filepath"
"strings"
"strings"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
...
@@ -30,8 +34,146 @@ func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInf
...
@@ -30,8 +34,146 @@ func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInf
}
}
func
(
a
*
Adaptor
)
ConvertImageRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
ImageRequest
)
(
any
,
error
)
{
func
(
a
*
Adaptor
)
ConvertImageRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
ImageRequest
)
(
any
,
error
)
{
//TODO implement me
switch
info
.
RelayMode
{
return
nil
,
errors
.
New
(
"not implemented"
)
case
constant
.
RelayModeImagesEdits
:
var
requestBody
bytes
.
Buffer
writer
:=
multipart
.
NewWriter
(
&
requestBody
)
writer
.
WriteField
(
"model"
,
request
.
Model
)
// 获取所有表单字段
formData
:=
c
.
Request
.
PostForm
// 遍历表单字段并打印输出
for
key
,
values
:=
range
formData
{
if
key
==
"model"
{
continue
}
for
_
,
value
:=
range
values
{
writer
.
WriteField
(
key
,
value
)
}
}
// Parse the multipart form to handle both single image and multiple images
if
err
:=
c
.
Request
.
ParseMultipartForm
(
32
<<
20
);
err
!=
nil
{
// 32MB max memory
return
nil
,
errors
.
New
(
"failed to parse multipart form"
)
}
if
c
.
Request
.
MultipartForm
!=
nil
&&
c
.
Request
.
MultipartForm
.
File
!=
nil
{
// Check if "image" field exists in any form, including array notation
var
imageFiles
[]
*
multipart
.
FileHeader
var
exists
bool
// First check for standard "image" field
if
imageFiles
,
exists
=
c
.
Request
.
MultipartForm
.
File
[
"image"
];
!
exists
||
len
(
imageFiles
)
==
0
{
// If not found, check for "image[]" field
if
imageFiles
,
exists
=
c
.
Request
.
MultipartForm
.
File
[
"image[]"
];
!
exists
||
len
(
imageFiles
)
==
0
{
// If still not found, iterate through all fields to find any that start with "image["
foundArrayImages
:=
false
for
fieldName
,
files
:=
range
c
.
Request
.
MultipartForm
.
File
{
if
strings
.
HasPrefix
(
fieldName
,
"image["
)
&&
len
(
files
)
>
0
{
foundArrayImages
=
true
for
_
,
file
:=
range
files
{
imageFiles
=
append
(
imageFiles
,
file
)
}
}
}
// If no image fields found at all
if
!
foundArrayImages
&&
(
len
(
imageFiles
)
==
0
)
{
return
nil
,
errors
.
New
(
"image is required"
)
}
}
}
// Process all image files
for
i
,
fileHeader
:=
range
imageFiles
{
file
,
err
:=
fileHeader
.
Open
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to open image file %d: %w"
,
i
,
err
)
}
defer
file
.
Close
()
// If multiple images, use image[] as the field name
fieldName
:=
"image"
if
len
(
imageFiles
)
>
1
{
fieldName
=
"image[]"
}
// Determine MIME type based on file extension
mimeType
:=
detectImageMimeType
(
fileHeader
.
Filename
)
// Create a form file with the appropriate content type
h
:=
make
(
textproto
.
MIMEHeader
)
h
.
Set
(
"Content-Disposition"
,
fmt
.
Sprintf
(
`form-data; name="%s"; filename="%s"`
,
fieldName
,
fileHeader
.
Filename
))
h
.
Set
(
"Content-Type"
,
mimeType
)
part
,
err
:=
writer
.
CreatePart
(
h
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"create form part failed for image %d: %w"
,
i
,
err
)
}
if
_
,
err
:=
io
.
Copy
(
part
,
file
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"copy file failed for image %d: %w"
,
i
,
err
)
}
}
// Handle mask file if present
if
maskFiles
,
exists
:=
c
.
Request
.
MultipartForm
.
File
[
"mask"
];
exists
&&
len
(
maskFiles
)
>
0
{
maskFile
,
err
:=
maskFiles
[
0
]
.
Open
()
if
err
!=
nil
{
return
nil
,
errors
.
New
(
"failed to open mask file"
)
}
defer
maskFile
.
Close
()
// Determine MIME type for mask file
mimeType
:=
detectImageMimeType
(
maskFiles
[
0
]
.
Filename
)
// Create a form file with the appropriate content type
h
:=
make
(
textproto
.
MIMEHeader
)
h
.
Set
(
"Content-Disposition"
,
fmt
.
Sprintf
(
`form-data; name="mask"; filename="%s"`
,
maskFiles
[
0
]
.
Filename
))
h
.
Set
(
"Content-Type"
,
mimeType
)
maskPart
,
err
:=
writer
.
CreatePart
(
h
)
if
err
!=
nil
{
return
nil
,
errors
.
New
(
"create form file failed for mask"
)
}
if
_
,
err
:=
io
.
Copy
(
maskPart
,
maskFile
);
err
!=
nil
{
return
nil
,
errors
.
New
(
"copy mask file failed"
)
}
}
}
else
{
return
nil
,
errors
.
New
(
"no multipart form data found"
)
}
// 关闭 multipart 编写器以设置分界线
writer
.
Close
()
c
.
Request
.
Header
.
Set
(
"Content-Type"
,
writer
.
FormDataContentType
())
return
bytes
.
NewReader
(
requestBody
.
Bytes
()),
nil
default
:
return
request
,
nil
}
}
// detectImageMimeType determines the MIME type based on the file extension
func
detectImageMimeType
(
filename
string
)
string
{
ext
:=
strings
.
ToLower
(
filepath
.
Ext
(
filename
))
switch
ext
{
case
".jpg"
,
".jpeg"
:
return
"image/jpeg"
case
".png"
:
return
"image/png"
case
".webp"
:
return
"image/webp"
default
:
// Try to detect from extension if possible
if
strings
.
HasPrefix
(
ext
,
".jp"
)
{
return
"image/jpeg"
}
// Default to png as a fallback
return
"image/png"
}
}
}
func
(
a
*
Adaptor
)
Init
(
info
*
relaycommon
.
RelayInfo
)
{
func
(
a
*
Adaptor
)
Init
(
info
*
relaycommon
.
RelayInfo
)
{
...
@@ -46,6 +188,8 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
...
@@ -46,6 +188,8 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
return
fmt
.
Sprintf
(
"%s/api/v3/chat/completions"
,
info
.
BaseUrl
),
nil
return
fmt
.
Sprintf
(
"%s/api/v3/chat/completions"
,
info
.
BaseUrl
),
nil
case
constant
.
RelayModeEmbeddings
:
case
constant
.
RelayModeEmbeddings
:
return
fmt
.
Sprintf
(
"%s/api/v3/embeddings"
,
info
.
BaseUrl
),
nil
return
fmt
.
Sprintf
(
"%s/api/v3/embeddings"
,
info
.
BaseUrl
),
nil
case
constant
.
RelayModeImagesGenerations
:
return
fmt
.
Sprintf
(
"%s/api/v3/images/generations"
,
info
.
BaseUrl
),
nil
default
:
default
:
}
}
return
""
,
fmt
.
Errorf
(
"unsupported relay mode: %d"
,
info
.
RelayMode
)
return
""
,
fmt
.
Errorf
(
"unsupported relay mode: %d"
,
info
.
RelayMode
)
...
@@ -91,6 +235,8 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom
...
@@ -91,6 +235,8 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom
}
}
case
constant
.
RelayModeEmbeddings
:
case
constant
.
RelayModeEmbeddings
:
err
,
usage
=
openai
.
OpenaiHandler
(
c
,
resp
,
info
)
err
,
usage
=
openai
.
OpenaiHandler
(
c
,
resp
,
info
)
case
constant
.
RelayModeImagesGenerations
,
constant
.
RelayModeImagesEdits
:
err
,
usage
=
openai
.
OpenaiHandlerWithUsage
(
c
,
resp
,
info
)
}
}
return
return
}
}
...
...
relay/image_handler.go
View file @
4307065c
...
@@ -17,6 +17,8 @@ import (
...
@@ -17,6 +17,8 @@ import (
"one-api/setting"
"one-api/setting"
"strings"
"strings"
"one-api/relay/constant"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
)
)
...
@@ -44,6 +46,11 @@ func getAndValidImageRequest(c *gin.Context, info *relaycommon.RelayInfo) (*dto.
...
@@ -44,6 +46,11 @@ func getAndValidImageRequest(c *gin.Context, info *relaycommon.RelayInfo) (*dto.
if
imageRequest
.
N
==
0
{
if
imageRequest
.
N
==
0
{
imageRequest
.
N
=
1
imageRequest
.
N
=
1
}
}
if
info
.
ApiType
==
constant
.
APITypeVolcEngine
{
watermark
:=
formData
.
Has
(
"watermark"
)
imageRequest
.
Watermark
=
&
watermark
}
default
:
default
:
err
:=
common
.
UnmarshalBodyReusable
(
c
,
imageRequest
)
err
:=
common
.
UnmarshalBodyReusable
(
c
,
imageRequest
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
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