fix: preserve only originally-required properties in strict tool schemas (#471)
Fixes #430. In normalizeSchemaForOpenAI(), the strict branch was adding every property key to required[], including optional ones. This caused providers like Groq, Azure OpenAI, and others to reject valid tool calls with a 400 / tool_use_failed error because the model correctly omits optional arguments but the provider sees them as missing required fields. Root cause: the strict branch used `[...existingRequired, ...allKeys]` instead of `existingRequired.filter(k => k in normalizedProps)`. The Gemini branch already had the correct logic. Fix: align the strict branch with the Gemini branch — only keep properties that were already marked required in the original schema. The additionalProperties: false constraint is preserved as strict-mode providers still require it. Add regression test covering the Read tool schema (file_path required, offset/limit/pages optional).
This commit is contained in:
committed by
GitHub
parent
2caf2fd982
commit
ccaa193eec
@@ -421,11 +421,13 @@ function normalizeSchemaForOpenAI(
|
||||
record.properties = normalizedProps
|
||||
|
||||
if (strict) {
|
||||
// OpenAI strict mode requires every property to be listed in required[]
|
||||
const allKeys = Object.keys(normalizedProps)
|
||||
record.required = Array.from(new Set([...existingRequired, ...allKeys]))
|
||||
// OpenAI strict mode requires additionalProperties: false on all object
|
||||
// schemas — override unconditionally to ensure nested objects comply.
|
||||
// Keep only the properties that were originally marked required in the schema.
|
||||
// Adding every property to required[] (the previous behaviour) caused strict
|
||||
// OpenAI-compatible providers (Groq, Azure, etc.) to reject tool calls because
|
||||
// the model correctly omits optional arguments — but the provider treats them
|
||||
// as missing required fields and returns a 400 / tool_use_failed error.
|
||||
record.required = existingRequired.filter(k => k in normalizedProps)
|
||||
// additionalProperties: false is still required by strict-mode providers.
|
||||
record.additionalProperties = false
|
||||
} else {
|
||||
// For Gemini: keep only existing required keys that are present in properties
|
||||
|
||||
Reference in New Issue
Block a user