loopd: the tag check asks /tokenize twice, not once per tag
Found in the M5b device check on straylight: the self-test asked about each of the 33 tags and each neutralised form, 66 requests, one connection each, and inferproxy's token bucket (burst 10) refused the ninth, so loopd would not start. Now it asks about all the tags at once, one per line, and then all their neutralised forms: the answer to the first must hold each tag's recorded token in order, the second none of them. Each tag is a special token, split out before anything else, so the lines between cannot change it; and a neutralised form is only dangerous as a tag's own token, so that is what the second answer must not hold. The errors name a tag by its id, as before. The fake tokenizer reads text with several tags in it; the tests cover each tag as ordinary tokens, as another listed or unlisted token, a duplicated token in the middle and at the end, a form holding each tag, and both requests failing. Spec section 8 and the runbook entry follow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+24
-25
@@ -735,21 +735,20 @@ if it was real.
|
||||
then `selftest: FAILED: <reason>; see docs/runbook.md#loopd-tags-unexpected`, and exits 1. No
|
||||
prompt was sent. The reason names a tag by its token id, never by its text:
|
||||
|
||||
- `the tag with id <id> is <n> tokens, not one`
|
||||
- `the tag with id <id> is one token, but the token <other>`
|
||||
- `the neutralised form of the tag with id <id> does not split into several tokens (the server
|
||||
gave <n>)`
|
||||
- `could not tokenize the tag with id <id>: <error>`
|
||||
- `the tag with id <id> does not tokenize as its recorded token`
|
||||
- `a neutralised tag tokenizes to the tag with id <id>`
|
||||
- `could not tokenize the tags: <error>`, or `… the neutralised tags: <error>`
|
||||
|
||||
**Why.** `loopd` neutralises the model's chat-template tags in every text from outside, writing
|
||||
their angle brackets as `<` and `>` (M5b spec, sections 6.2 and 8). The list of tags, each
|
||||
with its token id, is compiled into `bxcore` (`crates/bxcore/src/sanitize/tags.rs`), as measured
|
||||
on straylight. At startup `loopd` asks the server's `/tokenize` about each tag and its neutralised
|
||||
form. If a tag is no longer the one token recorded with it, the model or its tokenizer has changed
|
||||
and the list may no longer match it; if a neutralised form is a single token, neutralising no
|
||||
longer keeps that tag from the model. Either way the model could receive text from outside as its
|
||||
own control tokens, so `loopd` does not start. The check cannot find a new tag missing from the
|
||||
list: the server does not list its special tokens.
|
||||
with its token id, is compiled into `bxcore` (`crates/bxcore/src/sanitize/tags.rs`), as measured on
|
||||
straylight. At startup `loopd` asks the server's `/tokenize` about all the tags at once, one per
|
||||
line, then about all their neutralised forms: two requests, so that the check fits in `inferproxy`'s
|
||||
burst of connections. If a tag is no longer the token recorded with it, the model or its tokenizer
|
||||
has changed and the list may no longer match it; if a neutralised form comes back as a tag's token,
|
||||
neutralising no longer keeps that tag from the model. Either way the model could receive text from
|
||||
outside as its own control tokens, so `loopd` does not start. The check cannot find a new tag
|
||||
missing from the list: the server does not list its special tokens.
|
||||
|
||||
**Confirm.** Find the tag: `grep -n '<id>' crates/bxcore/src/sanitize/tags.rs` shows its entry,
|
||||
built from its name with `bar!`, `open!` or `close!`. Ask the server yourself, building the tag
|
||||
@@ -763,23 +762,23 @@ printf '{"model":"ornith-1.5-35b-a3b","content":"<|%s|>","parse_special":t
|
||||
```
|
||||
|
||||
For an `open!` tag the content is `<%s>`, for a `close!` tag `</%s>`. The first answer should be
|
||||
`{"tokens":[<id>]}`, the second a list of several ids. Check which model file the server loaded:
|
||||
`curl -s 'http://straylight:11434/props?model=ornith-1.5-35b-a3b' | jq .model_path`.
|
||||
`{"tokens":[<id>]}`, the second a list of ids without `<id>` in it. Check which model file the
|
||||
server loaded: `curl -s 'http://straylight:11434/props?model=ornith-1.5-35b-a3b' | jq .model_path`.
|
||||
|
||||
**Fix, by reason.**
|
||||
|
||||
- `could not tokenize …`: the server did not answer `/tokenize`. A router in front of
|
||||
llama-server may not forward it (crossbar does not, `docs/decisions.md`, P22): point
|
||||
`inferproxy` at llama-server itself. Otherwise check that the server is up:
|
||||
- `could not tokenize …`: the server did not answer `/tokenize`. A router in front of llama-server
|
||||
may not forward it (crossbar does not, `docs/decisions.md`, P22): point `inferproxy` at
|
||||
llama-server itself. Otherwise check that the server is up:
|
||||
`curl -s http://straylight:11434/health`, and run `loopd selftest --config <path>` again.
|
||||
- A tag with another id, or several tokens: the model file changed. Do not run `loopd` against
|
||||
it. Measure the new model's tags as M5b task 03 did (every vocabulary entry of type control or
|
||||
user-defined, each checked with `/tokenize`), and change `TAGS` and the tests' list in
|
||||
`crates/bxcore/tests/support/tags.rs` together; the change reaches the agent when it is rebuilt.
|
||||
A new model also invalidates the measurements in `docs/inference-contract.md`.
|
||||
- A neutralised form that is one token: the tokenizer has an entry for the escaped spelling, and
|
||||
the replacement of the M5b spec, section 6.2, no longer works for that tag. That is a design
|
||||
change: record it in `docs/decisions.md` and do not run `loopd` until it is settled.
|
||||
- A tag that does not tokenize as its recorded token: the model file changed. Do not run `loopd`
|
||||
against it. Measure the new model's tags as M5b task 03 did (every vocabulary entry of type
|
||||
control or user-defined, each checked with `/tokenize`), and change `TAGS` and the tests' list in
|
||||
`crates/bxcore/tests/support/tags.rs` together; the change reaches the agent when it is rebuilt. A
|
||||
new model also invalidates the measurements in `docs/inference-contract.md`.
|
||||
- A neutralised tag that tokenizes to a tag: the tokenizer reads the escaped spelling as the tag,
|
||||
and the replacement of the M5b spec, section 6.2, no longer works for it. That is a design change:
|
||||
record it in `docs/decisions.md` and do not run `loopd` until it is settled.
|
||||
|
||||
**Check.** `loopd selftest --config <path>` prints `selftest: ok`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user