A question that offers real choices, and says how many you may take
Three things about `ask_user`, all of them about the card being answerable rather than about the tool being callable. Options are required now, and they are objects: a label, and a line of description where the label alone does not say what choosing it would mean. "Rewrite it" and "Patch it" are two words that do not tell you which one loses your uncommitted work. They stack one per line, because a row of chips has nowhere to put the second line and no room to read the first. The model says whether they are exclusive. Only it knows whether its options are alternatives or a set, and the card has to show which -- a radio group offered where checkboxes were meant loses every answer but one. Exclusive is the default, being the cheaper mistake. A `multiple` question posts the same field name once per ticked box, so the endpoint gathers choices into a list; the `setdefault` it did before kept the first and dropped the rest, which is an answer that says something the reader did not. And "Something else" is added here, on every question, with the box behind it revealed by `:has()` and no JavaScript at all. The model is told never to write an "other" option of its own, because its version would be a choice with no box behind it -- a word submitted that means nothing. It carries a sentinel rather than an answer, and the endpoint swaps in what was typed beside it, or drops it when the box was left empty rather than telling the model the answer is "__other__". Typing no longer beats picking. That rule belonged to a box that was always visible next to the options; this one only exists once its own option is chosen, so picking is the answer and the box is one of the things you can pick. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,27 +31,56 @@
|
||||
|
||||
{% if ask.kind == "question" %}
|
||||
{% for item in ask.items %}
|
||||
{# Stacked, one per line, never in a row. An option carries a label and
|
||||
often a description, and a row of chips has nowhere to put the second
|
||||
and no room to read the first.
|
||||
|
||||
Radio or checkbox by what the model said: `multiple` is false for
|
||||
alternatives and true for a set. The name is the same either way, so
|
||||
the endpoint reads one value or several without knowing which it
|
||||
asked for. #}
|
||||
<fieldset class="interaction__question">
|
||||
<legend class="interaction__title">{{ item.title }}</legend>
|
||||
|
||||
{% if item.options %}
|
||||
<div class="interaction__options">
|
||||
{% for option in item.options %}
|
||||
<label class="chip">
|
||||
<input type="radio" name="choice.{{ item.key }}" value="{{ option }}">
|
||||
<span>{{ option }}</span>
|
||||
<label class="interaction__option">
|
||||
<input type="{{ 'checkbox' if item.multiple else 'radio' }}"
|
||||
name="choice.{{ item.key }}" value="{{ option.label }}">
|
||||
<span class="interaction__option-body">
|
||||
<span class="interaction__option-name">{{ option.label }}</span>
|
||||
{% if option.description %}
|
||||
<span class="interaction__option-note">{{ option.description }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if item.allow_free_text %}
|
||||
{# Never type="password". A model talked into asking for a credential
|
||||
must not be handed a field that looks built for one, and a chat
|
||||
transcript is not a place to keep secrets. #}
|
||||
<input class="input" type="text" name="text.{{ item.key }}" autocomplete="off"
|
||||
placeholder="{{ 'Or write your own answer…' if item.options else 'Your answer…' }}">
|
||||
{% endif %}
|
||||
{% if item.allow_free_text %}
|
||||
{# Added here and nowhere else: on every question, by this template,
|
||||
never by the model. That is why the tool tells it not to write an
|
||||
"other" option of its own -- two of them is one that does nothing,
|
||||
and its version would have no box behind it.
|
||||
|
||||
The box is revealed by `:has()` on the row, so this needs no
|
||||
JavaScript and cannot fall out of step with the checkbox. It is
|
||||
always submitted; the endpoint uses it only when this option is
|
||||
actually chosen. #}
|
||||
<label class="interaction__option interaction__option--other">
|
||||
<input type="{{ 'checkbox' if item.multiple else 'radio' }}"
|
||||
name="choice.{{ item.key }}" value="{{ other_value }}">
|
||||
<span class="interaction__option-body">
|
||||
<span class="interaction__option-name">Something else</span>
|
||||
{# Never type="password". A model talked into asking for a
|
||||
credential must not be handed a field that looks built for one,
|
||||
and a chat transcript is not a place to keep secrets. #}
|
||||
<input class="input interaction__other-input" type="text"
|
||||
name="text.{{ item.key }}" autocomplete="off"
|
||||
placeholder="Your own answer…">
|
||||
</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user