Summary
The tool description compressor rebuilds each tool's input_schema rather than copying it. Keywords that are not on its allowlist do not survive, so a caller's parameter constraints are absent from the upstream request. The call returns 2xx.
Root cause
Verified at 23cc328c1838. src/prompts/system.js:87
const input_schema = tool.input_schema || { type: "object", properties: {} };
const compressed = {
...
input_schema: {
type: input_schema.type || "object",
properties: {},
required: input_schema.required || [],
}
};
...
if (input_schema.properties) {
for (const [key, value] of Object.entries(input_schema.properties)) {
compressed.input_schema.properties[key] = { ... };
}
}
The rebuild keeps type, properties and required. It does not carry strict from the tool level, and the per-property copy does not carry pattern, const or oneOf. Called from src/orchestrator/index.js:1565.
Captured
Client:
{"model": "<provider-model>", "messages": [{"role": "user", "content": "G1 controlled request."}],
"tools": [{"type": "function", "function": {"name": "tool_a", "description": "Controlled G1 tool.",
"strict": true, "parameters": {"type": "object", "properties": {}}}}]}
The forwarded request carries the rebuilt schema with no strict.
Reproduction
Built from this repository at an unmodified commit. The upstream is a recording endpoint that stores the exact request bytes it receives and returns a fixed valid response.
curl -X POST http://localhost:<port>/v1/chat/completions \
-H 'content-type: application/json' -H "authorization: Bearer $KEY" \
-d '{"model":"<provider-model>","messages":[{"role":"user","content":"hi"}],
"tools":[{"type":"function","function":{"name":"tool_a","description":"t","strict":true,
"parameters":{"type":"object","properties":{"recipient":{"type":"string","pattern":"^(alice|bob)$"}},"required":["recipient"]}}}]}'
Compare pattern and strict in the outbound request.
Expected
Compression is aimed at description length, so the schema does not need rebuilding to achieve it. Copying input_schema through and compressing only the text fields would keep the constraints. If the rebuild is needed for another reason, then the keyword list should at least cover the enforceable ones, and a caller whose constraint is dropped should be told rather than getting a 2xx.
Summary
The tool description compressor rebuilds each tool's
input_schemarather than copying it. Keywords that are not on its allowlist do not survive, so a caller's parameter constraints are absent from the upstream request. The call returns 2xx.Root cause
Verified at
23cc328c1838.src/prompts/system.js:87The rebuild keeps
type,propertiesandrequired. It does not carrystrictfrom the tool level, and the per-property copy does not carrypattern,constoroneOf. Called fromsrc/orchestrator/index.js:1565.Captured
Client:
{"model": "<provider-model>", "messages": [{"role": "user", "content": "G1 controlled request."}], "tools": [{"type": "function", "function": {"name": "tool_a", "description": "Controlled G1 tool.", "strict": true, "parameters": {"type": "object", "properties": {}}}}]}The forwarded request carries the rebuilt schema with no
strict.Reproduction
Built from this repository at an unmodified commit. The upstream is a recording endpoint that stores the exact request bytes it receives and returns a fixed valid response.
Compare
patternandstrictin the outbound request.Expected
Compression is aimed at description length, so the schema does not need rebuilding to achieve it. Copying
input_schemathrough and compressing only the text fields would keep the constraints. If the rebuild is needed for another reason, then the keyword list should at least cover the enforceable ones, and a caller whose constraint is dropped should be told rather than getting a 2xx.