Model Answer Continuation in DocsFab
Look up how empty replies, length stops, and cumulative output authority affect a model answer.
An empty provider turn is replayed under its own, narrower bound. A completion
that carries a finish_reason and usage but no content and no tool call at all
is replaced by an identical replay, up to 3 attempts (maxBlankAttempts), with
the same backoff, Retry-After, deadline and cancellation handling. Empty turns
keep the narrower bound because the provider bills each one for its whole input
context, which a transport fault that never reached the model does not cost. A
turn that stopped for a reason of its own is never replayed identically: any
finish_reason outside the ordinary stop and tool-call set such as
content_filter, and a completion that carried provider error text, are
answers about that request, and repeating it cannot change them. A replayed
empty turn still costs tokens, so they are charged to the execution lease
against the governed turn that lands, and the turn record carries
providerBlankTurns with the number of empty turns observed, including the
last one when the budget runs out. The step then fails naming the streak and
the provider’s own stop reason, for example
Provider turn failed: blank: 3 blank provider turns stopReason=stop finish_reason=stop.
A turn that stopped at the output limit (finish_reason=length) is handled by
shape, on whichever step it lands. It is the terminal-answer pattern of any
step whose final report outgrows the limit. An empty one is a turn where the
provider billed the whole output budget and surfaced neither text nor a tool
call (a front door that withholds the cut answer instead of streaming it). Such
a turn is replayed exactly once, and not identically. The replay appends one
bounded user message naming the authorized limit and asking for small tool
arguments and a terminal answer that fits. It runs under the same veto, backoff
budget, deadline and cancellation gates, and counts as one providerRetries
and one providerBlankTurns. The turn record’s finalPayloadSha256 then names
the nudged payload that was actually sent, and a replay that reaches the
payload hook without its nudge fails closed. From that point every further
replay of the same turn repeats the nudged request: a transient fault or an
ordinary empty turn on the nudged attempt is replayed under the ordinary bounds
(8 transport attempts, 3 empty turns), with the nudge still in place. If the
replay is cut the same way
the step fails as
Provider turn failed: provider-failed: output-truncated: 2 provider turns of step review-documentation hit the 8,192-token output limit (finish_reason=length), last turn consumed 8,192 output tokens, no text or tool call surfaced; ...,
classified malformed-response; retryable=false: the step, the limit and the
tokens the cut turn consumed are all in the one line. A terminal answer whose
text was cut is never replayed (its text has already been published) and is
never accepted as complete. A step whose reply the runtime decodes against a
host response contract first asks for the rest of that answer inside the same
attempt, up to inference.maxLengthStopContinuations continuation turns (1 by
default). When no continuation is left, the partial text stays the turn’s
content and the step fails as text-truncated, naming the step, the limit, the
characters retained and the tokens consumed.
The two judgment steps of review-documentation over a version 2
source-obligation ledger (provisional-technical-verification and
verify-technical-accuracy) do not fail at either point. When their answer is
cut with no continuation left, or a cut turn and its replay surfaced nothing
(output-truncated), the runtime completes the judgment itself in the following way:
- It keeps each complete element it can recover from the retained text, read up to the composition’s response byte ceiling.
- It records every obligation without a complete judgment as unverified, and
sets the verdict to
human-review. - It adds one limitation that starts with
Host judgment completion:and names the limit, the continuation turns, the output tokens billed, the characters retained, and what it kept and filled.
If the step’s own checks refuse a kept element for its content, such as an
unknown severity or an overlong reason, the runtime keeps none of the cut
answer, records every obligation as unverified, and says so in the limitation.
The same floor applies when a continuation completes the cut answer but the
stitched reply is refused for its model content, even on an early attempt with
retries available. The host does not retry the whole step after that refusal.
The limitation and unverified obligation records name the continued refusal
without claiming that the retry budget was exhausted. A credential disclosure
or refusal of the host-filled result still fails the step. The attempt ends
succeeded, and its turn record keeps the length stop. A reading turn cut
with nothing surfaced is completed the same way, so an all-unverified judgment
does not by itself mean that an answer was started.
The same two steps also keep an uncut answer from failing the review when no
retry remains. An answer that the step’s own checks refuse for its content is
retried with the rule’s hint, as on every read-only step, within
retry.maxRetries. When no retry is left, because the attempt is the last one
or because the refusal is one that is never retried, such as an issue located
outside the target pages or a host filesystem path, the runtime keeps none of
the refused answer and records the same all-unverified judgment in the following way:
- Every obligation and every target page is unverified, with no issue
judgment, and the verdict is
human-review. - One limitation that starts with
Host judgment completion:names the step, the attempts spent, and the refusal’s rejection code, for examplejudgment-documentation-disposition-invalid. - Each demoted dimension names
judgment-reply-refusedas its reason.
Only a credential in the answer still fails the step. The host-filled judgment
has to fit the step’s own size and count bounds.
A documentation-set review of a large repository makes every documentation
page a target, and the host-filled coverage alone can exceed
technicalCorrection.proposalMaxBytes. The run then stops before its first
model call with technical-correction-proposal-max-bytes-bound, which names the
observed bytes and the bound. When the host-filled judgment crosses a byte
bound at the step instead, the step stops with the same kind of code and is not
retried. To avoid this, narrow the run to a focused-set scope, as described in
Review Scope.
A streamed tool call cut at the limit is
refused by Pi’s own agent loop, which asks the model to re-issue it; the
runtime grants that once and names a second consecutive cut as
tool-arguments-truncated rather than spending a third output budget. The
remedy is the composition’s maxOutputTokens or a shorter answer for that
step, which is why each message names the number. That remedy does not apply
when the cut turn was clamped to the cumulative output lease described in the
next paragraph. The lease remainder was the binding limit, so raising
maxOutputTokens does not move it. The message then names the remainder and
the lease limit instead, for example
... Shorten the answer; the limit was the 10,000 output tokens left of the 4,194,304-token cumulative output lease, not maxOutputTokens.,
and a shorter answer for that step is the remedy.
Each step attempt also carries a cumulative output-token lease (4,194,304 tokens
by default on the composition execution path). Before every provider turn, the runtime compares
that lease’s remainder with the output the turn would request. While the remainder covers
the per-turn output limit, nothing changes. Once it is smaller, the request’s
max_tokens is clamped to the exact remainder and the turn record carries
outputAuthority with the lease limit, the output already spent, and the floor.
Once the remainder falls below the floor (the provider’s minimum output plus
1,024 tokens) no request is sent: the turn record ends with reason
output-authority-exhausted, the lease ends budget-stopped under the same
terminal reason (distinct from cumulative-output-limit, which names a spend
that reached the limit), and the step fails naming the step, the output spent
and the lease limit, for example
Output authority exhausted [output-authority-exhausted]: step evaluate-document-quality used 4193804 of 4194304 cumulative output tokens; the 500 remaining are below the 1040-token floor, so no further provider turn was requested.
A stop the budget governor makes before a turn, such as the provider-turn
limit, records category context-exhausted and is never retried. The tool
turn that brings a step attempt’s executed calls to the hard tool-call ceiling
ends the attempt instead, with no answer asked for, because the step would
refuse an answer given at the ceiling. That attempt records category
no-progress with condition tool-call-ceiling, its lease ends
hard-tool-call-ceiling, and its code is
composition-execution-max-tool-calls-per-step-bound. The step retries it the
way it retries a read loop, as Read-Loop Supervision describes, from the same
retry.maxNoProgressRetries budget. A retry that reaches the ceiling again
fails the step with the same code. docsfab why names the owner by what set the
ceiling: the model’s progress for a sealed composition budget, and operator
configuration for a host limit or an override that lowered it.