Current behavior
When a provider returns tool-call arguments that are not valid JSON, the conversion does this (e.g. aimux-providers/src/openai/model.rs):
let input: Value = serde_json::from_str(&tc.function.arguments)
.unwrap_or_else(|_| Value::String(tc.function.arguments.clone()));
A malformed argument payload (truncated by max-tokens, trailing comma, unescaped quote — all common LLM failure modes) is silently wrapped as a JSON string and delivered as ToolCall.input. The caller expecting an object gets a string, and the failure surfaces far from its cause — in the user's tool executor, with no indication that the model emitted broken JSON.
There is also no validation that the called tool exists or that the input matches the tool's declared schema.
What the AI SDK does here (for comparison)
packages/ai/src/generate-text/parse-tool-call.ts:
- unknown tool name → typed
NoSuchToolError;
safeParseJSON + schema validation of the arguments → typed InvalidToolInputError;
- an optional
repairToolCall hook (ToolCallRepairFunction) that gets one chance to fix the broken call (structurally re-parse, or re-ask the model) before the typed error is thrown.
The interesting part: half the machinery already exists
aimux-core/src/json_repair.rs already ports the AI SDK's fix-json.ts and parse-partial-json.ts — but it is only wired into generate_object (repairing the text output before schema parsing). Tool-call arguments never touch it.
Question for the author
Is the current pass-through-and-degrade behavior a deliberate "keep the core thin, let callers validate" choice, or is tool-call parsing/repair simply not built yet?
If it's worth building, the natural shape would be:
- Parse + validate in the core tool-call path: malformed JSON → a typed error (or at minimum an explicit marker on the
ToolCall), unknown tool / schema mismatch → typed errors, instead of silent string degradation;
- Repair: try the existing
fix_json on malformed arguments first (free, already in-tree), and optionally expose a repair_tool_call hook for caller-driven repair (the AI SDK's repairToolCall equivalent);
- Streaming:
parse_partial_json is likewise already in-tree and could back partial tool-input parsing for ToolInputDelta consumers.
If it's deliberate, a doc note on ToolCall.input saying "may be a raw string when the provider emitted invalid JSON" would at least make the degradation visible.
References
aimux-providers/src/openai/model.rs — the unwrap_or_else(Value::String) fallback (same pattern in other providers)
aimux-core/src/json_repair.rs — existing fix_json / parse_partial_json ports, currently used only by generate_object
- AI SDK:
packages/ai/src/generate-text/parse-tool-call.ts, tool-call-repair-function.ts
Current behavior
When a provider returns tool-call arguments that are not valid JSON, the conversion does this (e.g.
aimux-providers/src/openai/model.rs):A malformed argument payload (truncated by max-tokens, trailing comma, unescaped quote — all common LLM failure modes) is silently wrapped as a JSON string and delivered as
ToolCall.input. The caller expecting an object gets a string, and the failure surfaces far from its cause — in the user's tool executor, with no indication that the model emitted broken JSON.There is also no validation that the called tool exists or that the input matches the tool's declared schema.
What the AI SDK does here (for comparison)
packages/ai/src/generate-text/parse-tool-call.ts:NoSuchToolError;safeParseJSON+ schema validation of the arguments → typedInvalidToolInputError;repairToolCallhook (ToolCallRepairFunction) that gets one chance to fix the broken call (structurally re-parse, or re-ask the model) before the typed error is thrown.The interesting part: half the machinery already exists
aimux-core/src/json_repair.rsalready ports the AI SDK'sfix-json.tsandparse-partial-json.ts— but it is only wired intogenerate_object(repairing the text output before schema parsing). Tool-call arguments never touch it.Question for the author
Is the current pass-through-and-degrade behavior a deliberate "keep the core thin, let callers validate" choice, or is tool-call parsing/repair simply not built yet?
If it's worth building, the natural shape would be:
ToolCall), unknown tool / schema mismatch → typed errors, instead of silent string degradation;fix_jsonon malformed arguments first (free, already in-tree), and optionally expose arepair_tool_callhook for caller-driven repair (the AI SDK'srepairToolCallequivalent);parse_partial_jsonis likewise already in-tree and could back partial tool-input parsing forToolInputDeltaconsumers.If it's deliberate, a doc note on
ToolCall.inputsaying "may be a raw string when the provider emitted invalid JSON" would at least make the degradation visible.References
aimux-providers/src/openai/model.rs— theunwrap_or_else(Value::String)fallback (same pattern in other providers)aimux-core/src/json_repair.rs— existingfix_json/parse_partial_jsonports, currently used only bygenerate_objectpackages/ai/src/generate-text/parse-tool-call.ts,tool-call-repair-function.ts