Summary
Constrain tool calls whose schema is only partially typed. Today (#374, PR #375) a tool is constrained only if its entire argument schema is fully constrainable; a single Any-typed value (no type), an open object (additionalProperties:true / no properties), or an untyped array drops the whole tool to unconstrained — including its required, well-typed keys.
Current behavior
GemmaToolArgumentConstraint.TryCompileObject / TryCompileNode return null (skip the tool) when any value isn't fully constrainable. Safe (never wedges, never wrong) but coarse: a tool like {location: string (required), context: object (open)} gets no constraint at all, so location can still be dropped.
Scope
Add a permissive "free value" frame to the automaton that accepts any single well-formed Gemma value (quoted string via the <|"|> token, bare scalar, {...}, [...]) and balances braces/brackets/quotes until the value ends — so the structure (required keys present, declared key names, the typed values) stays enforced while loosely-typed values are left free. Then compile partially-typed tools instead of skipping them.
Edge cases: brace/bracket/quote balancing for nested open objects; ensuring the free-value frame ends at the right delimiter so the enclosing object/array resumes correctly; keep allocation-free + default-off byte-identical.
Tests
Add mock + model-gated cases: a tool mixing a required typed key with an Any-typed / open-object value still forces the typed/required parts.
Follow-up to #374 / #375.
Summary
Constrain tool calls whose schema is only partially typed. Today (#374, PR #375) a tool is constrained only if its entire argument schema is fully constrainable; a single
Any-typed value (notype), an open object (additionalProperties:true/ noproperties), or an untyped array drops the whole tool to unconstrained — including its required, well-typed keys.Current behavior
GemmaToolArgumentConstraint.TryCompileObject/TryCompileNodereturnnull(skip the tool) when any value isn't fully constrainable. Safe (never wedges, never wrong) but coarse: a tool like{location: string (required), context: object (open)}gets no constraint at all, solocationcan still be dropped.Scope
Add a permissive "free value" frame to the automaton that accepts any single well-formed Gemma value (quoted string via the
<|"|>token, bare scalar,{...},[...]) and balances braces/brackets/quotes until the value ends — so the structure (required keys present, declared key names, the typed values) stays enforced while loosely-typed values are left free. Then compile partially-typed tools instead of skipping them.Edge cases: brace/bracket/quote balancing for nested open objects; ensuring the free-value frame ends at the right delimiter so the enclosing object/array resumes correctly; keep allocation-free + default-off byte-identical.
Tests
Add mock + model-gated cases: a tool mixing a required typed key with an
Any-typed / open-object value still forces the typed/required parts.Follow-up to #374 / #375.