Skip to content

arcint 1.2.3 — a loop guard for small models

Choose a tag to compare

@marfrit marfrit released this 24 Oct 10:17
· 498 commits to main since this release

This is not the current release. The current one is here, with its changes in CHANGELOG.md.

A loop guard for small models and two sampling rows by phase, all behind
switches that are off by default; with none set, a request is served as
in 1.2.2. The coder unit turns on the loop guard and the non-thinking row
(operator, 2026-10-24).

  • Loop guard — --loop-guard off|detect|nudge|slap|ladder (request
    field loop_guard; /props sampler_defaults.loop_guard). A detector on
    the reasoning's lines (stripped, longer than 20 characters) keeps the share
    of repeated lines over the newest --loop-guard-window 200; a line repeats
    when it equals an earlier one or, under --loop-guard-match near (the
    default; exact the alternative), has a character-3-gram Jaccard of at
    least 0.8 with one of the last 400 distinct lines. At
    --loop-guard-threshold 0.8 (hysteresis 0.7) the ladder climbs: a fixed
    sentence is injected into the reasoning once (nudge); after
    --loop-guard-grace 40 more looping lines the think block's closer is
    forced by token id (slap); a looping answer after the slap ends the turn
    with finish_reason loop (abort). A forced token ends a speculative
    verify walk at its position. The response's loop_guard object and the
    decode line count detections, nudges, slaps and aborts. The test and what
    it does not heal: README, "New: loop guard for small models".
  • Non-thinking row — --nothink-temp, --nothink-top-p,
    --nothink-top-k, --nothink-min-p, --nothink-presence-penalty: a
    request that resolves to thinking off starts from this row instead of the
    thinking row; the request's own fields still win; /props reports it.
    On the coder the card's non-thinking row (0.7 / 0.8 / 20, presence 0 as
    measured) lifted the sampled acceptance task from a mean of 9.056 (7 of 90
    runs at 0/10) to 9.922 (none of 90 at 0), the shortfall 1.2.2's release
    check named.
  • Answer row — --answer-temp, --answer-top-p, --answer-top-k,
    --answer-min-p, --answer-presence-penalty,
    --answer-frequency-penalty: in a thinking turn, the sampler switches to
    this row once the think block closes (the presence and frequency penalties
    count the answer only). Measured without effect on the coder after a full
    reasoning (the answer follows the reasoning); shipped as a switch, not set
    on any unit.
  • The loop guard's nudge no longer leaves its last piece at the start of the
    next reasoning line the detector judges.

Runtime: llama.cpp 3e4dfa8 with contrib/llama.cpp/patches (60
patches), unchanged from 1.2.2; dependencies unchanged. arcint-stt (1.2.0)
and arcint-imggen (1.1.1) are unchanged.

Release check (measured-here, 2026-10-24, the pre-tag package): the
device-free suite 286 cases and ctest -L unit 6/6, the package build green.
Same-window speed A/B against 1.2.2 with every switch off: the coder (A770)
and the agent (B60) pass with byte-identical texts. On the coder with its
1.2.3 flags (the ladder, the non-thinking row): the acceptance task 5/5 at
10/10 greedy and 30/30 at 10/10 at temperature 0.7; the agent 3/3 at 10/10.
The loop guard's false-positive run completed: 21 of 21 tasks, 19 fully
correct, 1 wrong with no guard event, 1 ended on loop after a real loop in
the reasoning and again in the answer. Not run: Flash-Next's A/B (its sampler
path is the agent's, every switch off; code).

Published (2026-10-24): arcint 1.2.3-1 built by its recipe from the
v1.2.3 tag tarball (sha256 bf0f02f4…) and published to the trixie
repository (index verified); installed from the pool on the card host. The
agent runs it with the switches off (the stop-string check, the acceptance
task 10/10); the coder's unit carries --loop-guard ladder and the
non-thinking row and takes them at its next start. arcint-stt stays at
1.2.0-1, arcint-imggen at 1.1.1-1.