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
269a1e12
authored
Jun 02, 2025
by
Apple\Apple
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/main' into ui/refactor
parents
36a9781d
c668362f
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
125 additions
and
72 deletions
+125
-72
relay/channel/gemini/relay-gemini.go
+122
-72
relay/relay-image.go
+3
-0
No files found.
relay/channel/gemini/relay-gemini.go
View file @
269a1e12
...
@@ -18,6 +18,24 @@ import (
...
@@ -18,6 +18,24 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
)
)
var
geminiSupportedMimeTypes
=
map
[
string
]
bool
{
"application/pdf"
:
true
,
"audio/mpeg"
:
true
,
"audio/mp3"
:
true
,
"audio/wav"
:
true
,
"image/png"
:
true
,
"image/jpeg"
:
true
,
"text/plain"
:
true
,
"video/mov"
:
true
,
"video/mpeg"
:
true
,
"video/mp4"
:
true
,
"video/mpg"
:
true
,
"video/avi"
:
true
,
"video/wmv"
:
true
,
"video/mpegps"
:
true
,
"video/flv"
:
true
,
}
// Setting safety to the lowest possible values since Gemini is already powerless enough
// Setting safety to the lowest possible values since Gemini is already powerless enough
func
CovertGemini2OpenAI
(
textRequest
dto
.
GeneralOpenAIRequest
,
info
*
relaycommon
.
RelayInfo
)
(
*
GeminiChatRequest
,
error
)
{
func
CovertGemini2OpenAI
(
textRequest
dto
.
GeneralOpenAIRequest
,
info
*
relaycommon
.
RelayInfo
)
(
*
GeminiChatRequest
,
error
)
{
...
@@ -215,14 +233,20 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
...
@@ -215,14 +233,20 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
}
}
// 判断是否是url
// 判断是否是url
if
strings
.
HasPrefix
(
part
.
GetImageMedia
()
.
Url
,
"http"
)
{
if
strings
.
HasPrefix
(
part
.
GetImageMedia
()
.
Url
,
"http"
)
{
// 是url,获取
图片
的类型和base64编码的数据
// 是url,获取
文件
的类型和base64编码的数据
fileData
,
err
:=
service
.
GetFileBase64FromUrl
(
part
.
GetImageMedia
()
.
Url
)
fileData
,
err
:=
service
.
GetFileBase64FromUrl
(
part
.
GetImageMedia
()
.
Url
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get file base64 from url
failed: %s"
,
err
.
Error
()
)
return
nil
,
fmt
.
Errorf
(
"get file base64 from url
'%s' failed: %w"
,
part
.
GetImageMedia
()
.
Url
,
err
)
}
}
// 校验 MimeType 是否在 Gemini 支持的白名单中
if
_
,
ok
:=
geminiSupportedMimeTypes
[
strings
.
ToLower
(
fileData
.
MimeType
)];
!
ok
{
return
nil
,
fmt
.
Errorf
(
"MIME type '%s' from URL '%s' is not supported by Gemini. Supported types are: %v"
,
fileData
.
MimeType
,
part
.
GetImageMedia
()
.
Url
,
getSupportedMimeTypesList
())
}
parts
=
append
(
parts
,
GeminiPart
{
parts
=
append
(
parts
,
GeminiPart
{
InlineData
:
&
GeminiInlineData
{
InlineData
:
&
GeminiInlineData
{
MimeType
:
fileData
.
MimeType
,
MimeType
:
fileData
.
MimeType
,
// 使用原始的 MimeType,因为大小写可能对API有意义
Data
:
fileData
.
Base64Data
,
Data
:
fileData
.
Base64Data
,
},
},
})
})
...
@@ -291,100 +315,126 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
...
@@ -291,100 +315,126 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
return
&
geminiRequest
,
nil
return
&
geminiRequest
,
nil
}
}
// Helper function to get a list of supported MIME types for error messages
func
getSupportedMimeTypesList
()
[]
string
{
keys
:=
make
([]
string
,
0
,
len
(
geminiSupportedMimeTypes
))
for
k
:=
range
geminiSupportedMimeTypes
{
keys
=
append
(
keys
,
k
)
}
return
keys
}
// cleanFunctionParameters recursively removes unsupported fields from Gemini function parameters.
// cleanFunctionParameters recursively removes unsupported fields from Gemini function parameters.
func
cleanFunctionParameters
(
params
interface
{})
interface
{}
{
func
cleanFunctionParameters
(
params
interface
{})
interface
{}
{
if
params
==
nil
{
if
params
==
nil
{
return
nil
return
nil
}
}
paramMap
,
ok
:=
params
.
(
map
[
string
]
interface
{})
switch
v
:=
params
.
(
type
)
{
if
!
ok
{
case
map
[
string
]
interface
{}
:
// Not a map, return as is (e.g., could be an array or primitive)
// Create a copy to avoid modifying the original
return
params
cleanedMap
:=
make
(
map
[
string
]
interface
{})
}
for
k
,
val
:=
range
v
{
cleanedMap
[
k
]
=
val
}
// Create a copy to avoid modifying the original
// Remove unsupported root-level fields
cleanedMap
:=
make
(
map
[
string
]
interface
{})
delete
(
cleanedMap
,
"default"
)
for
k
,
v
:=
range
paramMap
{
delete
(
cleanedMap
,
"exclusiveMaximum"
)
cleanedMap
[
k
]
=
v
delete
(
cleanedMap
,
"exclusiveMinimum"
)
}
delete
(
cleanedMap
,
"$schema"
)
delete
(
cleanedMap
,
"additionalProperties"
)
// Remove unsupported root-level fields
// Check and clean 'format' for string types
delete
(
cleanedMap
,
"default"
)
if
propType
,
typeExists
:=
cleanedMap
[
"type"
]
.
(
string
);
typeExists
&&
propType
==
"string"
{
delete
(
cleanedMap
,
"exclusiveMaximum"
)
if
formatValue
,
formatExists
:=
cleanedMap
[
"format"
]
.
(
string
);
formatExists
{
delete
(
cleanedMap
,
"exclusiveMinimum"
)
if
formatValue
!=
"enum"
&&
formatValue
!=
"date-time"
{
delete
(
cleanedMap
,
"$schema"
)
delete
(
cleanedMap
,
"format"
)
delete
(
cleanedMap
,
"additionalProperties"
)
}
}
}
// Clean properties
// Clean properties
if
props
,
ok
:=
cleanedMap
[
"properties"
]
.
(
map
[
string
]
interface
{});
ok
&&
props
!=
nil
{
if
props
,
ok
:=
cleanedMap
[
"properties"
]
.
(
map
[
string
]
interface
{});
ok
&&
props
!=
nil
{
cleanedProps
:=
make
(
map
[
string
]
interface
{})
cleanedProps
:=
make
(
map
[
string
]
interface
{})
for
propName
,
propValue
:=
range
props
{
for
propName
,
propValue
:=
range
props
{
propMap
,
ok
:=
propValue
.
(
map
[
string
]
interface
{})
cleanedProps
[
propName
]
=
cleanFunctionParameters
(
propValue
)
if
!
ok
{
cleanedProps
[
propName
]
=
propValue
// Keep non-map properties
continue
}
}
cleanedMap
[
"properties"
]
=
cleanedProps
}
// Create a copy of the property map
// Recursively clean items in arrays
cleanedPropMap
:=
make
(
map
[
string
]
interface
{})
if
items
,
ok
:=
cleanedMap
[
"items"
]
.
(
map
[
string
]
interface
{});
ok
&&
items
!=
nil
{
for
k
,
v
:=
range
propMap
{
cleanedMap
[
"items"
]
=
cleanFunctionParameters
(
items
)
cleanedPropMap
[
k
]
=
v
}
// Also handle items if it's an array of schemas
if
itemsArray
,
ok
:=
cleanedMap
[
"items"
]
.
([]
interface
{});
ok
{
cleanedItemsArray
:=
make
([]
interface
{},
len
(
itemsArray
))
for
i
,
item
:=
range
itemsArray
{
cleanedItemsArray
[
i
]
=
cleanFunctionParameters
(
item
)
}
}
cleanedMap
[
"items"
]
=
cleanedItemsArray
}
// Remove unsupported fields
// Recursively clean other schema composition keywords
delete
(
cleanedPropMap
,
"default"
)
for
_
,
field
:=
range
[]
string
{
"allOf"
,
"anyOf"
,
"oneOf"
}
{
delete
(
cleanedPropMap
,
"exclusiveMaximum"
)
if
nested
,
ok
:=
cleanedMap
[
field
]
.
([]
interface
{});
ok
{
delete
(
cleanedPropMap
,
"exclusiveMinimum"
)
cleanedNested
:=
make
([]
interface
{},
len
(
nested
))
delete
(
cleanedPropMap
,
"$schema"
)
for
i
,
item
:=
range
nested
{
delete
(
cleanedPropMap
,
"additionalProperties"
)
cleanedNested
[
i
]
=
cleanFunctionParameters
(
item
)
// Check and clean 'format' for string types
if
propType
,
typeExists
:=
cleanedPropMap
[
"type"
]
.
(
string
);
typeExists
&&
propType
==
"string"
{
if
formatValue
,
formatExists
:=
cleanedPropMap
[
"format"
]
.
(
string
);
formatExists
{
if
formatValue
!=
"enum"
&&
formatValue
!=
"date-time"
{
delete
(
cleanedPropMap
,
"format"
)
}
}
}
cleanedMap
[
field
]
=
cleanedNested
}
}
}
// Recursively clean nested properties within this property if it's an object/array
// Recursively clean patternProperties
// Check the type before recursing
if
patternProps
,
ok
:=
cleanedMap
[
"patternProperties"
]
.
(
map
[
string
]
interface
{});
ok
{
if
propType
,
typeExists
:=
cleanedPropMap
[
"type"
]
.
(
string
);
typeExists
&&
(
propType
==
"object"
||
propType
==
"array"
)
{
cleanedPatternProps
:=
make
(
map
[
string
]
interface
{})
cleanedProps
[
propName
]
=
cleanFunctionParameters
(
cleanedPropMap
)
for
pattern
,
schema
:=
range
patternProps
{
}
else
{
cleanedPatternProps
[
pattern
]
=
cleanFunctionParameters
(
schema
)
cleanedProps
[
propName
]
=
cleanedPropMap
// Assign the cleaned map back if not recursing
}
}
cleanedMap
[
"patternProperties"
]
=
cleanedPatternProps
}
// Recursively clean definitions
if
definitions
,
ok
:=
cleanedMap
[
"definitions"
]
.
(
map
[
string
]
interface
{});
ok
{
cleanedDefinitions
:=
make
(
map
[
string
]
interface
{})
for
defName
,
defSchema
:=
range
definitions
{
cleanedDefinitions
[
defName
]
=
cleanFunctionParameters
(
defSchema
)
}
cleanedMap
[
"definitions"
]
=
cleanedDefinitions
}
}
cleanedMap
[
"properties"
]
=
cleanedProps
}
// Recursively clean items in arrays if needed (e.g., type: array, items: { ... })
// Recursively clean $defs (newer JSON Schema draft)
if
items
,
ok
:=
cleanedMap
[
"items"
]
.
(
map
[
string
]
interface
{});
ok
&&
items
!=
nil
{
if
defs
,
ok
:=
cleanedMap
[
"$defs"
]
.
(
map
[
string
]
interface
{});
ok
{
cleanedMap
[
"items"
]
=
cleanFunctionParameters
(
items
)
cleanedDefs
:=
make
(
map
[
string
]
interface
{})
}
for
defName
,
defSchema
:=
range
defs
{
// Also handle items if it's an array of schemas
cleanedDefs
[
defName
]
=
cleanFunctionParameters
(
defSchema
)
if
itemsArray
,
ok
:=
cleanedMap
[
"items"
]
.
([]
interface
{});
ok
{
}
cleanedItemsArray
:=
make
([]
interface
{},
len
(
itemsArray
))
cleanedMap
[
"$defs"
]
=
cleanedDefs
for
i
,
item
:=
range
itemsArray
{
cleanedItemsArray
[
i
]
=
cleanFunctionParameters
(
item
)
}
}
cleanedMap
[
"items"
]
=
cleanedItemsArray
}
// Recursively clean other schema composition keywords if necessary
// Clean conditional keywords
for
_
,
field
:=
range
[]
string
{
"allOf"
,
"anyOf"
,
"oneOf"
}
{
for
_
,
field
:=
range
[]
string
{
"if"
,
"then"
,
"else"
,
"not"
}
{
if
nested
,
ok
:=
cleanedMap
[
field
]
.
([]
interface
{});
ok
{
if
nested
,
ok
:=
cleanedMap
[
field
];
ok
{
cleanedNested
:=
make
([]
interface
{},
len
(
nested
))
cleanedMap
[
field
]
=
cleanFunctionParameters
(
nested
)
for
i
,
item
:=
range
nested
{
cleanedNested
[
i
]
=
cleanFunctionParameters
(
item
)
}
}
cleanedMap
[
field
]
=
cleanedNested
}
}
}
return
cleanedMap
return
cleanedMap
case
[]
interface
{}
:
// Handle arrays of schemas
cleanedArray
:=
make
([]
interface
{},
len
(
v
))
for
i
,
item
:=
range
v
{
cleanedArray
[
i
]
=
cleanFunctionParameters
(
item
)
}
return
cleanedArray
default
:
// Not a map or array, return as is (e.g., could be a primitive)
return
params
}
}
}
func
removeAdditionalPropertiesWithDepth
(
schema
interface
{},
depth
int
)
interface
{}
{
func
removeAdditionalPropertiesWithDepth
(
schema
interface
{},
depth
int
)
interface
{}
{
...
...
relay/relay-image.go
View file @
269a1e12
...
@@ -41,6 +41,9 @@ func getAndValidImageRequest(c *gin.Context, info *relaycommon.RelayInfo) (*dto.
...
@@ -41,6 +41,9 @@ func getAndValidImageRequest(c *gin.Context, info *relaycommon.RelayInfo) (*dto.
imageRequest
.
Quality
=
"standard"
imageRequest
.
Quality
=
"standard"
}
}
}
}
if
imageRequest
.
N
==
0
{
imageRequest
.
N
=
1
}
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