Upstream source
Problem
Gemini functionDeclarations.parameters accepts only Gemini's OpenAPI-style schema subset. The current converter calls CleanFunctionParameters, which silently drops valid full-JSON-Schema keywords such as const, additionalProperties, oneOf, propertyNames, exclusiveMinimum, etc.
That changes tool semantics without returning an error. Example: a client schema that pins a tool argument with const can be forwarded as an empty {} schema, allowing values the caller explicitly prohibited.
Gemini also supports parametersJsonSchema, which can carry the full schema. Upstream #7537 starts using that field whenever cleaning would otherwise remove unsupported keywords.
api.lmm.best gap
Current main (517d29ed1d575add0796be0fb166b426b9987f2f when this issue was opened) still has the same vulnerable conversion paths:
apps/api-go/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go calls sharedgemini.CleanFunctionParameters directly.
apps/api-go/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go does the same.
apps/api-go/relaykit/relayconvert/internal/shared/gemini/schema.go drops non-allowlisted keywords.
- LMM currently has no
parametersJsonSchema output field in dto.FunctionRequest.
This is distinct from #479. #479 sanitizes invalid required: null values before strict providers see them; this issue is about preserving valid JSON Schema constraints when converting to Gemini.
Why not copy upstream #7537 yet
The upstream patch is still open and its current head has unresolved review findings that can still weaken schemas:
- The unsupported-keyword detector stops at the depth cutoff before inspecting deeper nodes, so constraints below that boundary may still be stripped.
- Tuple-form
items: [...] currently checks only the first item; unsupported keywords in later tuple entries can be missed.
EncodeFunctionParametersForGemini checks properties: {} before unsupported keywords, so a constrained empty object such as { "type":"object", "properties":{}, "additionalProperties":false } can lose the constraint entirely.
Upstream CI is also action_required for the forked PR, while CodeRabbit explicitly reports moderate merge risk. Porting that exact patch now would copy known correctness gaps into LMM.
Recommended LMM-native implementation
Once the upstream review findings are resolved (or independently implement them here):
- Add
ParametersJsonSchema any \json:"parametersJsonSchema,omitempty"`` to the Gemini function declaration representation used by LMM.
- Centralize selection in one helper:
- if the schema contains only Gemini
parameters-safe keywords, preserve the existing cleaned parameters path;
- if any reachable schema node contains a keyword that cleaning would remove, emit the original schema as
parametersJsonSchema and omit parameters;
- never emit both fields.
- Detect unsupported constraints across all reachable schema-bearing locations used here, including object properties, every array/tuple item and combinators used by the converter.
- Keep recursion bounded/cycle-safe without silently treating uninspected deep descendants as safe.
- Test unsupported-keyword detection before the existing
properties: {} omission so constrained empty objects remain constrained.
- Wire the helper through OpenAI Chat → Gemini, Responses → Gemini and generic
toolconv → Gemini paths.
- Do not alter raw same-protocol pass-through behavior, pricing, groups, model price lock,
/fast, OAuth restrictions or the administrator AI assistant.
Acceptance criteria
- A nested
const survives conversion to Gemini via parametersJsonSchema.
additionalProperties: false, oneOf, propertyNames and numeric constraints that the current cleaner would remove are preserved.
- Tuple-form
items preserves an unsupported constraint in any tuple element, not only index 0.
- A constrained empty object (
properties: {} plus an unsupported constraint) is preserved rather than omitted.
- A schema with only Gemini-supported fields continues to use cleaned
parameters and retains current type normalization behavior.
- A genuinely empty allowlisted object keeps the current omission behavior where required for Gemini compatibility.
- The converter never emits both
parameters and parametersJsonSchema for one declaration.
- Deep schemas do not silently lose constraints at the recursion boundary; tests cover the chosen depth/cycle policy.
- OpenAI Chat → Gemini, Responses → Gemini and generic relayconvert/toolconv paths all have regression coverage.
- Existing relaykit tests and Go build pass.
Validation status
Static comparison confirms LMM still contains the affected cleaner/call sites and has no existing parametersJsonSchema implementation or duplicate PR. No code PR is opened in this run because the only upstream implementation currently has unresolved correctness findings; this issue records the stable behavior target so we can port the corrected form rather than the known-incomplete patch.
Upstream source
6fb97c0149e9a010d6ea9dd824e4d1d27c47d17brelaykit/dto/openai_request.gorelaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.gorelaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.gorelaykit/relayconvert/internal/shared/gemini/schema.gorelaykit/relayconvert/internal/toolconv/encode.goProblem
Gemini
functionDeclarations.parametersaccepts only Gemini's OpenAPI-style schema subset. The current converter callsCleanFunctionParameters, which silently drops valid full-JSON-Schema keywords such asconst,additionalProperties,oneOf,propertyNames,exclusiveMinimum, etc.That changes tool semantics without returning an error. Example: a client schema that pins a tool argument with
constcan be forwarded as an empty{}schema, allowing values the caller explicitly prohibited.Gemini also supports
parametersJsonSchema, which can carry the full schema. Upstream #7537 starts using that field whenever cleaning would otherwise remove unsupported keywords.api.lmm.best gap
Current
main(517d29ed1d575add0796be0fb166b426b9987f2fwhen this issue was opened) still has the same vulnerable conversion paths:apps/api-go/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.gocallssharedgemini.CleanFunctionParametersdirectly.apps/api-go/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.godoes the same.apps/api-go/relaykit/relayconvert/internal/shared/gemini/schema.godrops non-allowlisted keywords.parametersJsonSchemaoutput field indto.FunctionRequest.This is distinct from #479. #479 sanitizes invalid
required: nullvalues before strict providers see them; this issue is about preserving valid JSON Schema constraints when converting to Gemini.Why not copy upstream #7537 yet
The upstream patch is still open and its current head has unresolved review findings that can still weaken schemas:
items: [...]currently checks only the first item; unsupported keywords in later tuple entries can be missed.EncodeFunctionParametersForGeminichecksproperties: {}before unsupported keywords, so a constrained empty object such as{ "type":"object", "properties":{}, "additionalProperties":false }can lose the constraint entirely.Upstream CI is also
action_requiredfor the forked PR, while CodeRabbit explicitly reports moderate merge risk. Porting that exact patch now would copy known correctness gaps into LMM.Recommended LMM-native implementation
Once the upstream review findings are resolved (or independently implement them here):
ParametersJsonSchema any \json:"parametersJsonSchema,omitempty"`` to the Gemini function declaration representation used by LMM.parameters-safe keywords, preserve the existing cleanedparameterspath;parametersJsonSchemaand omitparameters;properties: {}omission so constrained empty objects remain constrained.toolconv→ Gemini paths./fast, OAuth restrictions or the administrator AI assistant.Acceptance criteria
constsurvives conversion to Gemini viaparametersJsonSchema.additionalProperties: false,oneOf,propertyNamesand numeric constraints that the current cleaner would remove are preserved.itemspreserves an unsupported constraint in any tuple element, not only index 0.properties: {}plus an unsupported constraint) is preserved rather than omitted.parametersand retains current type normalization behavior.parametersandparametersJsonSchemafor one declaration.Validation status
Static comparison confirms LMM still contains the affected cleaner/call sites and has no existing
parametersJsonSchemaimplementation or duplicate PR. No code PR is opened in this run because the only upstream implementation currently has unresolved correctness findings; this issue records the stable behavior target so we can port the corrected form rather than the known-incomplete patch.