feat: distinguish no-output retries from validation feedback in `RetryPromptPart`
- Closes #6404
This is the first of the two PRs discussed on the issue: the part-level cause split, independent of the renderer/provenance work.
What
RetryPromptPart.model_response() rendered every tool_name is None retry with a Validation feedback: prefix and a Fix the errors and try again. suffix. That framing is wrong for the no-actionable-output retry (thinking-only responses), where nothing failed validation and there is nothing to fix.
This adds a cause discriminator to RetryPromptPart so the two tool_name is None causes are distinguishable at the part level, and branches model_response() accordingly:
cause: Literal['error', 'no_output'] = 'error'. The default'error'reproduces today's behavior for every existing and deserialized part;'no_output'is set only at the no-actionable-output site in_agent_graph.py.- For
'no_output',model_response()returns the content verbatim (e.g.Please return text or call a tool.), without the validation prefix/suffix.
Naming: cause rather than kind to avoid confusion with the existing part_kind. Happy to rename if you'd prefer reason or something else.
Scope
Deliberately excludes the renderer (per-profile developer/system role + tag fallback at Model.prepare_messages) and the model-neutral source/provenance field, which are the follow-up PR still pending the tag-name and profile-flag decisions.
Backward compatibility
Per the version policy, adding an optional field with a default to an existing message part is not a breaking change. A test round-trips a cause-less history through ModelMessagesTypeAdapter and asserts it deserializes to 'error' and renders identically.
Notes
The snapshot updates show input_tokens dropping by ~8 on the follow-up request, since the no-output retry no longer carries the validation boilerplate.
Checklist
- Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
- No breaking changes in accordance with the version policy.
- PR title is fit for the release changelog.