Spaces:
Runtime error
Runtime error
| // Schema-aware repair of double-encoded tool-call arguments. | |
| // | |
| // Several free-tier models (GLM family prominently) emit NESTED JSON inside | |
| // tool arguments as a string: `{"plan": "[{\"step\":...}]"}` instead of | |
| // `{"plan": [{"step":...}]}`. Strict clients reject the call β observed in | |
| // production as Codex `failed to parse function arguments: invalid type: | |
| // string ..., expected a sequence`, which killed the agent turn right at its | |
| // status-update call. The gateway has the request's tool schemas, so it can | |
| // repair this principled-ly: only when the schema says a parameter is an | |
| // array/object AND the string value parses to exactly that JSON type. A | |
| // parameter whose schema says "string" is never touched, even if it looks | |
| // like JSON. | |
| // | |
| // Also handles whole-arguments double encoding (the arguments field itself | |
| // being a JSON-encoded string of a JSON object), which needs no schema. | |
| /** | |
| * Repair a tool call's `arguments` JSON string against the tool's parameter | |
| * schema. Returns the original string untouched whenever anything doesn't | |
| * parse or doesn't match β this must never corrupt a valid call. | |
| */ | |
| export function repairToolArguments(args, paramSchema) { | |
| let parsed; | |
| try { | |
| parsed = JSON.parse(args); | |
| } | |
| catch { | |
| return args; | |
| } | |
| let changed = false; | |
| // Whole-arguments double encoding: `"{\"a\":1}"` parses to a string that is | |
| // itself JSON of an object. Unwrap one level. | |
| if (typeof parsed === 'string') { | |
| try { | |
| const inner = JSON.parse(parsed); | |
| if (inner !== null && typeof inner === 'object' && !Array.isArray(inner)) { | |
| parsed = inner; | |
| changed = true; | |
| } | |
| else { | |
| return args; | |
| } | |
| } | |
| catch { | |
| return args; | |
| } | |
| } | |
| if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { | |
| return changed ? JSON.stringify(parsed) : args; | |
| } | |
| const props = paramSchema?.properties; | |
| if (props) { | |
| const obj = parsed; | |
| for (const [key, value] of Object.entries(obj)) { | |
| if (typeof value !== 'string') | |
| continue; | |
| const want = props[key]?.type; | |
| if (want !== 'array' && want !== 'object') | |
| continue; | |
| const trimmed = value.trim(); | |
| if (!(trimmed.startsWith('[') || trimmed.startsWith('{'))) | |
| continue; | |
| try { | |
| const inner = JSON.parse(trimmed); | |
| const isMatch = want === 'array' | |
| ? Array.isArray(inner) | |
| : inner !== null && typeof inner === 'object' && !Array.isArray(inner); | |
| if (isMatch) { | |
| obj[key] = inner; | |
| changed = true; | |
| } | |
| } | |
| catch { | |
| // Not actually JSON β leave the string alone. | |
| } | |
| } | |
| } | |
| return changed ? JSON.stringify(parsed) : args; | |
| } | |
| /** | |
| * Build a tool-name β parameter-schema map from an OpenAI-style tools array | |
| * (chat-completions shape: {type:'function', function:{name, parameters}}). | |
| */ | |
| export function toolSchemaMap(tools) { | |
| const map = new Map(); | |
| for (const t of tools ?? []) { | |
| const name = t.function?.name; | |
| if (t.type === 'function' && name && t.function?.parameters && typeof t.function.parameters === 'object') { | |
| map.set(name, t.function.parameters); | |
| } | |
| } | |
| return map; | |
| } | |
| //# sourceMappingURL=tool-args.js.map |