Skip to content

v0.1.9-alpha.1 — #111 tool-call attractor block

Choose a tag to compare

@Kaden-Schutt Kaden-Schutt released this 02 May 08:04
· 4121 commits to master since this release

v0.1.9-alpha.1 (2026-05-02)

Patch release. Closes #111 — MQ4 single-token attractor on <tool_call>
that left agentic harnesses unable to dispatch tool calls on
qwen3.6:27b.mq4 (and any other MQ4-quant Qwen3+ model with structured-
output drift). Two complementary defenses ship together:

  • Engine (daemon) — new GPU-side apply_unclosed_attractor_block
    scans the recent decode window for unclosed <tool_call> openers
    (opens − closes); when depth ≥ 2, writes a single 4-byte -INF
    to the logits buffer at the opener's token offset before the next
    gpu.sample_top_p. Same gate for <think> to head off the
    thinking-mode boundary corruption the same reporter saw. Cost is
    zero when not tripped, ~5 µs when tripped — no D2H, no kernel
    change. The (opens − closes) invariant means legitimate multi-
    tool turns never trip: a complete <tool_call>...</tool_call>
    decrements depth before the next opener arrives.

  • CLI (parser)parseToolCalls now strips any leading
    <tool_call>\s* repeats from a captured block before JSON parse.
    Defense-in-depth in case a nested opener does slip through (the
    engine block fires before the third opener, but the second still
    ships in the visible stream).

Verified on hardware (gfx1100 / 7900 XTX / ROCm 7.2 / qwen3.6:27b.mq4)
against the two prompts the reporter posted as still-broken after the
v0.1.9-alpha defensive parser ship:

Prompt 1: "what files are in this directory?"
→ tool_calls: [{ name: "bash", arguments: { command: "ls -la" } }]

Prompt 2: "write a file here named test.md with the text test inside"
→ tool_calls: [{ name: "write", arguments: { path: "test.md", content: "test" } }]

Both finish_reason: "tool_calls". No attractor loop, no nested
openers, no parser repair signal on stderr. The model emits clean spec
JSON now that the gate is in place.

Tests: 10 Rust unit tests (llama::tests) covering threshold edges,
window scope, complete-pair-allow, depth-saturate-at-zero. 14 Bun tests
(cli/parse_tool_calls.test.ts) including 4 new for nested-opener
strip. Coherence-gate green on 6/6 prompts including the existing
tool-call coverage test.

Still a stopgap on the symptom. The underlying root cause is MQ4
calibration drift on structured-output token positions; Path C
calibration retrain (#39) is the proper fix.