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
d694a197
authored
Jan 15, 2026
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: openAI function to gemini function field adjusted to whitelist mode
parent
2a15e3b1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
134 additions
and
67 deletions
+134
-67
relay/channel/gemini/relay-gemini.go
+134
-67
No files found.
relay/channel/gemini/relay-gemini.go
View file @
d694a197
...
@@ -655,117 +655,184 @@ func getSupportedMimeTypesList() []string {
...
@@ -655,117 +655,184 @@ func getSupportedMimeTypesList() []string {
return
keys
return
keys
}
}
var
geminiOpenAPISchemaAllowedFields
=
map
[
string
]
struct
{}{
"anyOf"
:
{},
"default"
:
{},
"description"
:
{},
"enum"
:
{},
"example"
:
{},
"format"
:
{},
"items"
:
{},
"maxItems"
:
{},
"maxLength"
:
{},
"maxProperties"
:
{},
"maximum"
:
{},
"minItems"
:
{},
"minLength"
:
{},
"minProperties"
:
{},
"minimum"
:
{},
"nullable"
:
{},
"pattern"
:
{},
"properties"
:
{},
"propertyOrdering"
:
{},
"required"
:
{},
"title"
:
{},
"type"
:
{},
}
const
geminiFunctionSchemaMaxDepth
=
64
// 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
{}
{
return
cleanFunctionParametersWithDepth
(
params
,
0
)
}
func
cleanFunctionParametersWithDepth
(
params
interface
{},
depth
int
)
interface
{}
{
if
params
==
nil
{
if
params
==
nil
{
return
nil
return
nil
}
}
if
depth
>=
geminiFunctionSchemaMaxDepth
{
return
cleanFunctionParametersShallow
(
params
)
}
switch
v
:=
params
.
(
type
)
{
switch
v
:=
params
.
(
type
)
{
case
map
[
string
]
interface
{}
:
case
map
[
string
]
interface
{}
:
//
Create a copy to avoid modifying the original
//
Keep only Gemini-supported OpenAPI schema subset fields (per official SDK Schema).
cleanedMap
:=
make
(
map
[
string
]
interface
{})
cleanedMap
:=
make
(
map
[
string
]
interface
{}
,
len
(
v
)
)
for
k
,
val
:=
range
v
{
for
k
,
val
:=
range
v
{
if
_
,
ok
:=
geminiOpenAPISchemaAllowedFields
[
k
];
ok
{
cleanedMap
[
k
]
=
val
cleanedMap
[
k
]
=
val
}
}
// Remove unsupported root-level fields
delete
(
cleanedMap
,
"default"
)
delete
(
cleanedMap
,
"exclusiveMaximum"
)
delete
(
cleanedMap
,
"exclusiveMinimum"
)
delete
(
cleanedMap
,
"$schema"
)
delete
(
cleanedMap
,
"additionalProperties"
)
delete
(
cleanedMap
,
"propertyNames"
)
// Check and clean 'format' for string types
if
propType
,
typeExists
:=
cleanedMap
[
"type"
]
.
(
string
);
typeExists
&&
propType
==
"string"
{
if
formatValue
,
formatExists
:=
cleanedMap
[
"format"
]
.
(
string
);
formatExists
{
if
formatValue
!=
"enum"
&&
formatValue
!=
"date-time"
{
delete
(
cleanedMap
,
"format"
)
}
}
}
}
normalizeGeminiSchemaTypeAndNullable
(
cleanedMap
)
// 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
{
cleanedProps
[
propName
]
=
cleanFunctionParameters
(
propValue
)
cleanedProps
[
propName
]
=
cleanFunctionParameters
WithDepth
(
propValue
,
depth
+
1
)
}
}
cleanedMap
[
"properties"
]
=
cleanedProps
cleanedMap
[
"properties"
]
=
cleanedProps
}
}
// Recursively clean items in arrays
// Recursively clean items in arrays
if
items
,
ok
:=
cleanedMap
[
"items"
]
.
(
map
[
string
]
interface
{});
ok
&&
items
!=
nil
{
if
items
,
ok
:=
cleanedMap
[
"items"
]
.
(
map
[
string
]
interface
{});
ok
&&
items
!=
nil
{
cleanedMap
[
"items"
]
=
cleanFunctionParameters
(
items
)
cleanedMap
[
"items"
]
=
cleanFunctionParameters
WithDepth
(
items
,
depth
+
1
)
}
}
// Also handle items if it's an array of schemas
// OpenAPI tuple-style items is not supported by Gemini SDK Schema; keep first to avoid API rejection.
if
itemsArray
,
ok
:=
cleanedMap
[
"items"
]
.
([]
interface
{});
ok
{
if
itemsArray
,
ok
:=
cleanedMap
[
"items"
]
.
([]
interface
{});
ok
&&
len
(
itemsArray
)
>
0
{
cleanedItemsArray
:=
make
([]
interface
{},
len
(
itemsArray
))
cleanedMap
[
"items"
]
=
cleanFunctionParametersWithDepth
(
itemsArray
[
0
],
depth
+
1
)
for
i
,
item
:=
range
itemsArray
{
cleanedItemsArray
[
i
]
=
cleanFunctionParameters
(
item
)
}
cleanedMap
[
"items"
]
=
cleanedItemsArray
}
}
// Recursively clean other schema composition keywords
// Recursively clean anyOf
for
_
,
field
:=
range
[]
string
{
"allOf"
,
"anyOf"
,
"oneOf"
}
{
if
nested
,
ok
:=
cleanedMap
[
"anyOf"
]
.
([]
interface
{});
ok
&&
nested
!=
nil
{
if
nested
,
ok
:=
cleanedMap
[
field
]
.
([]
interface
{});
ok
{
cleanedNested
:=
make
([]
interface
{},
len
(
nested
))
cleanedNested
:=
make
([]
interface
{},
len
(
nested
))
for
i
,
item
:=
range
nested
{
for
i
,
item
:=
range
nested
{
cleanedNested
[
i
]
=
cleanFunctionParameters
(
item
)
cleanedNested
[
i
]
=
cleanFunctionParametersWithDepth
(
item
,
depth
+
1
)
}
cleanedMap
[
field
]
=
cleanedNested
}
}
cleanedMap
[
"anyOf"
]
=
cleanedNested
}
}
// Recursively clean patternProperties
return
cleanedMap
if
patternProps
,
ok
:=
cleanedMap
[
"patternProperties"
]
.
(
map
[
string
]
interface
{});
ok
{
cleanedPatternProps
:=
make
(
map
[
string
]
interface
{})
case
[]
interface
{}
:
for
pattern
,
schema
:=
range
patternProps
{
// Handle arrays of schemas
cleanedPatternProps
[
pattern
]
=
cleanFunctionParameters
(
schema
)
cleanedArray
:=
make
([]
interface
{},
len
(
v
))
for
i
,
item
:=
range
v
{
cleanedArray
[
i
]
=
cleanFunctionParametersWithDepth
(
item
,
depth
+
1
)
}
}
cleanedMap
[
"patternProperties"
]
=
cleanedPatternProps
return
cleanedArray
default
:
// Not a map or array, return as is (e.g., could be a primitive)
return
params
}
}
}
// Recursively clean definitions
func
cleanFunctionParametersShallow
(
params
interface
{})
interface
{}
{
if
definitions
,
ok
:=
cleanedMap
[
"definitions"
]
.
(
map
[
string
]
interface
{});
ok
{
switch
v
:=
params
.
(
type
)
{
cleanedDefinitions
:=
make
(
map
[
string
]
interface
{})
case
map
[
string
]
interface
{}
:
for
defName
,
defSchema
:=
range
definitions
{
cleanedMap
:=
make
(
map
[
string
]
interface
{},
len
(
v
))
cleanedDefinitions
[
defName
]
=
cleanFunctionParameters
(
defSchema
)
for
k
,
val
:=
range
v
{
if
_
,
ok
:=
geminiOpenAPISchemaAllowedFields
[
k
];
ok
{
cleanedMap
[
k
]
=
val
}
}
cleanedMap
[
"definitions"
]
=
cleanedDefinitions
}
}
normalizeGeminiSchemaTypeAndNullable
(
cleanedMap
)
// Recursively clean $defs (newer JSON Schema draft)
// Stop recursion and avoid retaining huge nested structures.
if
defs
,
ok
:=
cleanedMap
[
"$defs"
]
.
(
map
[
string
]
interface
{});
ok
{
delete
(
cleanedMap
,
"properties"
)
cleanedDefs
:=
make
(
map
[
string
]
interface
{})
delete
(
cleanedMap
,
"items"
)
for
defName
,
defSchema
:=
range
defs
{
delete
(
cleanedMap
,
"anyOf"
)
cleanedDefs
[
defName
]
=
cleanFunctionParameters
(
defSchema
)
return
cleanedMap
case
[]
interface
{}
:
// Prefer an empty list over deep recursion on attacker-controlled inputs.
return
[]
interface
{}{}
default
:
return
params
}
}
cleanedMap
[
"$defs"
]
=
cleanedDefs
}
func
normalizeGeminiSchemaTypeAndNullable
(
schema
map
[
string
]
interface
{})
{
rawType
,
ok
:=
schema
[
"type"
]
if
!
ok
||
rawType
==
nil
{
return
}
}
// Clean conditional keywords
normalize
:=
func
(
t
string
)
(
string
,
bool
)
{
for
_
,
field
:=
range
[]
string
{
"if"
,
"then"
,
"else"
,
"not"
}
{
switch
strings
.
ToLower
(
strings
.
TrimSpace
(
t
))
{
if
nested
,
ok
:=
cleanedMap
[
field
];
ok
{
case
"object"
:
cleanedMap
[
field
]
=
cleanFunctionParameters
(
nested
)
return
"OBJECT"
,
false
case
"array"
:
return
"ARRAY"
,
false
case
"string"
:
return
"STRING"
,
false
case
"integer"
:
return
"INTEGER"
,
false
case
"number"
:
return
"NUMBER"
,
false
case
"boolean"
:
return
"BOOLEAN"
,
false
case
"null"
:
return
""
,
true
default
:
return
t
,
false
}
}
}
}
return
cleanedMap
switch
t
:=
rawType
.
(
type
)
{
case
string
:
normalized
,
isNull
:=
normalize
(
t
)
if
isNull
{
schema
[
"nullable"
]
=
true
delete
(
schema
,
"type"
)
return
}
schema
[
"type"
]
=
normalized
case
[]
interface
{}
:
case
[]
interface
{}
:
// Handle arrays of schemas
nullable
:=
false
cleanedArray
:=
make
([]
interface
{},
len
(
v
))
var
chosen
string
for
i
,
item
:=
range
v
{
for
_
,
item
:=
range
t
{
cleanedArray
[
i
]
=
cleanFunctionParameters
(
item
)
if
s
,
ok
:=
item
.
(
string
);
ok
{
normalized
,
isNull
:=
normalize
(
s
)
if
isNull
{
nullable
=
true
continue
}
if
chosen
==
""
{
chosen
=
normalized
}
}
}
if
nullable
{
schema
[
"nullable"
]
=
true
}
if
chosen
!=
""
{
schema
[
"type"
]
=
chosen
}
else
{
delete
(
schema
,
"type"
)
}
}
return
cleanedArray
default
:
// Not a map or array, return as is (e.g., could be a primitive)
return
params
}
}
}
}
...
...
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