Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 74 additions & 4 deletions src/Orbit.Api/OpenApi/AnswerComposition.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,77 @@ A single record selector uses `choice`, including when the client also supports
typed definition before negotiating a renderable single selector to `choice`.
Per-question free-text settings own question forms; the top-level flag applies to
choice only. Invalid ask calls return the precise failed constraint to the model
for one corrected call in the same turn, then prose fallback. No question prepares
a write. See [ask validation and bilingual copy](AskValidation.md).
and continue through the normal tool loop within its round and token limits.
The continuation can read, prepare a write preview, retry a necessary question once,
or answer in prose. Only a validated, accepted ask ends the reply with a question
block; an invalid ask never discards recovery tool calls. The ask retry limit applies
to question attempts, not other tools. No question prepares a write. See
[ask validation and bilingual copy](AskValidation.md).

An attempted choice retains option IDs that identify owned habits even when its
question fails validation and no habit query preceded it. The unanswered candidates
join the same selection state as account reads, so candidate-dependent writes stay
withheld before coalescing and preview creation. Recovery reads and previews for
unrelated targets still run. Only typed option IDs establish this state; option
labels and the person's message do not. Until a valid question is issued, the reply
reuses `chat.answer.selectHabit`: "Which habit would you like to choose?" in en and
"Qual hábito você quer escolher?" in pt-BR. No new person-visible copy is added.

The failed-ask recovery path adds the following model-facing diagnostics. These
are tool feedback, not fixed replies to the person. Existing question retry and
plain-text fallback instructions remain available.

| en | pt-BR |
| --- | --- |
| The ask retry limit has been reached. | O limite de tentativas de ask foi atingido. |
| A failed ask did not issue a question. Continue the original request with the appropriate tools when its inputs are clear; no ask is needed for an actionable request. | A chamada de ask que falhou não emitiu uma pergunta. Continue o pedido original com as ferramentas adequadas quando as informações estiverem claras; um pedido que já pode ser atendido não precisa de ask. |

Strict tool conversion requires every property on the wire and represents optional
values as nullable. Null means absent, with false as the existing default for optional
booleans; it does not satisfy a form's required input. The source schema and validator
still own field requirements. A question form needs its nonempty `questions` array.
Neither a title nor null placeholders supply a question. Clear requests, including
dated batches and habit-and-goal reads, use their normal tools without ask.

The following changed model instructions and schema descriptions are recorded for
copy review in both languages. The model receives the English tool instructions;
the Portuguese column records their meaning for review. No person-visible fallback
copy or public DTO changes in this round.

| Location | en | pt-BR |
| --- | --- | --- |
| Ask context | A valid, accepted ask ends the reply: put it last and make no further tool calls after acceptance. A failed ask returns validation feedback; continue the original request using that feedback. | Uma chamada válida e aceita de ask encerra a resposta: coloque-a por último e não faça outras chamadas após a aceitação. Se ask falhar, use o retorno da validação para continuar o pedido original. |
| Withheld call feedback | This call was withheld while ask was unresolved. Continue the original request using the ask result; prepare only changes whose inputs are resolved. | Esta chamada foi retida enquanto ask estava pendente. Continue o pedido original usando o resultado de ask; prepare apenas alterações com as informações necessárias já definidas. |
| Tool description prefix | Ask for missing input with a choice, a pick list for a bulk preview, or a multi-field question form. Read account data first. Only a valid, accepted ask ends the reply. Never request approval or repeat a preview question. | Peça as informações que faltam com uma escolha, uma lista de seleção para a prévia de um lote ou um formulário com várias perguntas. Leia primeiro os dados da conta. Só uma chamada válida e aceita de ask encerra a resposta. Nunca peça aprovação nem repita uma pergunta da prévia. |
| Tool description addition | Do not call ask for a clear request you can act on, including a batch of dated items or a request to show habits and goals. Null fields are absent; form question requires nonempty questions. | Não chame ask quando o pedido estiver claro e já puder ser atendido, incluindo um lote de itens com datas ou um pedido para mostrar hábitos e metas. Campos null estão ausentes; form question exige questions preenchido. |
| Tool description selection boundary | An explicit request for the person to choose habits requires ask, even with one candidate. Use pickList for selecting habits to log or skip, with action and items; it prepares no write. | Um pedido explícito para a pessoa escolher hábitos exige ask, mesmo com um único candidato. Use pickList para selecionar hábitos para registrar ou pular, com action e items; isso não prepara alterações. |
| question | Required nonempty question for choice, at most 120 characters. Use null for other forms. | Pergunta obrigatória e preenchida para choice, com até 120 caracteres. Use null nos outros formatos. |
| title | Required nonempty heading for pickList; optional heading of at most 120 characters for question. Null means absent. | Título obrigatório e preenchido para pickList; título opcional de até 120 caracteres para question. Null indica ausência. |
| multiSelect | For choice only; null means false. pickList always selects multiple items; question requires false or null. | Apenas para choice; null equivale a false. pickList sempre permite vários itens; question exige false ou null. |
| allowFreeText | For choice only; null means false. Must be false for pickList. In form question, each questions entry controls its own allowFreeText; this top-level value is ignored. | Apenas para choice; null equivale a false. Deve ser false em pickList. Em form question, cada entrada de questions controla seu próprio allowFreeText; o valor do nível superior é ignorado. |
| intro | Optional nonempty introduction of at most 500 characters for question only. Use null for choice and pickList. | Introdução opcional e preenchida, com até 500 caracteres, apenas para question. Use null em choice e pickList. |
| progress | For question only. Requires 0 <= current <= total and total >= 1. Null means absent. | Apenas para question. Exige 0 <= current <= total e total >= 1. Null indica ausência. |
| questions | Required for question only. Use unique nonempty question ids. singleChoice and multipleChoice require 2 to 10 options even with allowFreeText=true. scale requires lowLabel and highLabel. slider requires min, max, step and unit. yesNo has no options. Null means absent and is allowed only for other forms. Use null for fields belonging to another question type. | Obrigatório apenas para question. Use identificadores únicos e preenchidos. singleChoice e multipleChoice exigem de 2 a 10 opções, mesmo com allowFreeText=true. scale exige lowLabel e highLabel. slider exige min, max, step e unit. yesNo não tem opções. Null indica ausência e só é aceito nos outros formatos. Use null nos campos de outro tipo de pergunta. |
| questions[].options | Required for singleChoice and multipleChoice. Null means absent for scale, slider and yesNo. Option ids must be unique within the question. | Obrigatório para singleChoice e multipleChoice. Null indica ausência em scale, slider e yesNo. Os identificadores das opções devem ser únicos em cada pergunta. |
| questions[].options[].recommended | At most one recommended option, only for singleChoice. Null means false. | No máximo uma opção recomendada, apenas em singleChoice. Null equivale a false. |
| questions[].allowFreeText | Allow a custom answer for singleChoice, multipleChoice, or yesNo. Null means false; must be false or null for scale and slider. | Permita uma resposta livre em singleChoice, multipleChoice ou yesNo. Null equivale a false; em scale e slider, deve ser false ou null. |
| questions[].lowLabel | Required nonempty label for 1 on a scale, at most 120 characters. Use null for other types. | Rótulo obrigatório e preenchido para o valor 1 da escala, com até 120 caracteres. Use null nos outros tipos. |
| questions[].highLabel | Required nonempty label for 5 on a scale, at most 120 characters. Use null for other types. | Rótulo obrigatório e preenchido para o valor 5 da escala, com até 120 caracteres. Use null nos outros tipos. |
| questions[].min | Required slider lower bound. Use null for other types. | Limite inferior obrigatório para slider. Use null nos outros tipos. |
| questions[].max | Required slider upper bound, greater than min. Use null for other types. | Limite superior obrigatório para slider, maior que min. Use null nos outros tipos. |
| questions[].step | Required positive slider step dividing max minus min exactly. Use null for other types. | Passo positivo obrigatório para slider, que divida exatamente a diferença entre max e min. Use null nos outros tipos. |
| questions[].unit | Required nonempty slider unit in the person's language, at most 24 characters. Use null for other types. | Unidade obrigatória e preenchida para slider, no idioma da pessoa, com até 24 caracteres. Use null nos outros tipos. |
| options | Required for choice. Null means absent for other forms. | Obrigatório para choice. Null indica ausência nos outros formatos. |
| options[].description; items[].detail | Supporting text, at most 24 characters. Null means absent. | Texto de apoio com até 24 caracteres. Null indica ausência. |
| action | Required bulk action for pickList. Null means absent for other forms. | Ação em lote obrigatória para pickList. Null indica ausência nos outros formatos. |
| items | Required for pickList. Null means absent for other forms. | Obrigatório para pickList. Null indica ausência nos outros formatos. |

The retained recovery suffixes are also model-facing:

| en | pt-BR |
| --- | --- |
| Retry ask once with valid arguments and a supported form: {forms}. choice requires question and options; pickList requires title, action and items; question requires questions. | Tente ask mais uma vez com argumentos válidos e um formato aceito: {forms}. choice exige question e options; pickList exige title, action e items; question exige questions. |
| Ask in plain text instead. | Faça a pergunta em texto simples. |

Supported data answers start with the typed block and omit framing prose. Choice questions stay in their blocks; pending previews retain
their existing narrative suppression. One short status may explain
Expand Down Expand Up @@ -146,8 +215,9 @@ for the selection contract and the English and Brazilian Portuguese copy submitt
An issued `ask` block ends the turn and suppresses read completion and readable read
fallbacks. Its question and options are the model's structured output, preserved without
message-based rewriting. A rejected ask can retry once with a supported form and valid
arguments. Only a lone model-emitted ask call can use that retry; it cannot dispatch a write
or restart the general tool loop. An unavailable ask surface falls back to text.
arguments through the normal tool loop. Recovery can read or prepare independent changes;
unanswered candidate selections continue to guard dependent writes. An unavailable ask
surface falls back to text.
Ask instructions name the client's supported forms while the shared
tool schemas remain stable across clients. A model-emitted
single-choice question can use the existing choice surface when question blocks are unsupported;
Expand Down
7 changes: 5 additions & 2 deletions src/Orbit.Api/OpenApi/AskValidation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ requirements as well as field bounds. The strict provider schema retains these
descriptions; optional null placeholders are removed before deserialization.
Domain guards remain the source of the constraints and now identify the failing
field. FluentValidation returns that reason through the tool result. The chat
loop sends it back to the model, permits one corrected ask, then asks for prose.
loop sends it back to the model and continues the original request within its turn
limits. A necessary ask can be corrected once; otherwise the model uses normal
tools or prose. Only a valid, accepted ask ends the reply. See the
[failed-ask recovery contract and current bilingual schema copy](AnswerComposition.md).

A question form's top-level `allowFreeText` is ignored; each question's setting
controls its answer. The optional heading comes from `title`, then `question`.
Expand Down Expand Up @@ -64,7 +67,7 @@ questions remain unchanged.

| en | pt-BR |
| --- | --- |
| Ask for missing input with a choice, a pick list for a bulk preview, or a multi-field question form. Read account data first. Ends the reply. Never request approval or repeat a preview question. | Peça os dados que faltam com uma escolha, uma lista de seleção para uma prévia em lote ou um formulário com vários campos. Consulte os dados da conta primeiro. Encerra a resposta. Nunca peça aprovação nem repita uma pergunta já feita pela prévia. |
| Ask for missing input with a choice, a pick list for a bulk preview, or a multi-field question form. Read account data first. Only a valid, accepted ask ends the reply. Never request approval or repeat a preview question. | Peça os dados que faltam com uma escolha, uma lista de seleção para uma prévia em lote ou um formulário com vários campos. Consulte os dados da conta primeiro. Só uma chamada válida e aceita de ask encerra a resposta. Nunca peça aprovação nem repita uma pergunta já feita pela prévia. |
| A request to choose habits requires ask, even with one candidate. Use pickList for selecting habits to log or skip, with action and items; it prepares no write. | Um pedido para escolher hábitos exige ask, mesmo com apenas uma opção. Use pickList para selecionar hábitos que serão registrados ou pulados, com action e items; isso não prepara nenhuma alteração. |
| For a single choice among existing records, use form choice with top-level question and options, even when form question is supported. Reserve form question for multiple fields. If the change is also missing, ask for both target and change in the choice question with allowFreeText=true. Use only a form the client supports. | Para uma única escolha entre registros existentes, use form choice com question e options no nível superior, mesmo quando form question estiver disponível. Reserve form question para vários campos. Se a alteração também não estiver definida, pergunte qual registro e o que mudar na pergunta de choice, com allowFreeText=true. Use apenas um formato aceito pelo cliente. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal sealed class ToolExecutionAccumulator
public int AskAttempts { get; set; }
public bool CanRetryAsk { get; set; }
public HabitSelectionCandidates? PendingSelection { get; set; }
public List<HabitSelectionCandidates> AmbiguousReadCandidates { get; } = [];
public List<HabitSelectionCandidates> SelectionCandidates { get; } = [];
public bool HasBudgetRecovery { get; set; }
public bool IsPreviewDiscussion { get; set; }
public string? PreviewDiscussionText { get; private set; }
Expand Down
Loading
Loading