An effort the model had never heard of

Reported from a live instance, on Bonsai:

  Jinja Exception: Unexpected reasoning effort high. Supported types are
  xhigh (default), medium, and low.

Effort goes out two ways because no single field works, and the second --
chat_template_kwargs -- is not a parameter the server interprets. It is
rendered into the model's own chat template, which does not ignore a value it
does not know: it calls raise_exception, and the request dies before a token.
So a perfectly ordinary option, drawn by this application in its own menu, took
the whole reply with it.

The vocabulary is per model and nobody agrees. gpt-oss takes low/medium/high.
Bonsai takes low/medium/xhigh and refuses high. OpenAI has added minimal, xhigh
and max at different points, and which of them a given model accepts varies
again. One global tuple was going to be wrong for somebody whatever it held.

A model carries its own list now, and the picker, the slash command and the
request builder all read it. A column rather than a key in capabilities_json,
for the reason context_length is one: that dict is rebuilt wholesale from the
submitted checkboxes on every save.

And it corrects itself. A refusal retries the reply once without the effort
rather than losing it -- safe only because the template renders before any
token, so nothing has been emitted, and there is a guard that keeps it that way
-- then narrows the model's list. Bonsai's error states what it does take, so
that is what gets stored.

Note the parser bug, because it is a good one: "high" is a substring of
"xhigh", so reading the advertised list by substring learned `high` from a
sentence explaining that `high` is the problem. Whole words now, with a test
named after it.

/effort reads its levels off the picker instead of a second copy of the list
kept in the browser.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 21:20:06 +00:00
co-authored by Claude Opus 5
parent b7bf7d728b
commit 32e2326d41
10 changed files with 382 additions and 21 deletions
@@ -104,11 +104,39 @@
</p>
</div>
<div class="field">
<span class="field__label">Reasoning efforts this model accepts</span>
<div class="btn-row">
{% for value in efforts %}
<label class="checkbox">
<input type="checkbox" name="reasoning_efforts" value="{{ value }}"
{{ 'checked' if value in model_efforts }}>
<span class="mono">{{ value }}</span>
</label>
{% endfor %}
</div>
<p class="field__hint">
The vocabulary is <strong>not the same for every model</strong>, and
sending one a model does not know is not ignored — it is rendered into
the model's chat template, which raises and fails the whole reply.
gpt-oss takes <span class="mono">low/medium/high</span>; Bonsai takes
<span class="mono">low/medium/xhigh</span> and refuses
<span class="mono">high</span>; OpenAI has added
<span class="mono">minimal</span>, <span class="mono">xhigh</span> and
<span class="mono">max</span> at various points.
<br>
Tick none and the common three are offered, which is right for almost
everything. If an endpoint ever refuses one anyway, that reply is
retried without it and this list corrects itself — so this is worth
setting by hand only to save that one round trip.
</p>
</div>
<div class="field">
<label class="field__label" for="default-effort">Default reasoning effort</label>
<select class="select" id="default-effort" name="default_effort">
<option value="">None — send nothing</option>
{% for value in efforts %}
{% for value in model_efforts %}
<option value="{{ value }}"
{{ 'selected' if model.params_json.get('reasoning_effort') == value }}>
{{ value }}