Getting started · Read the stop reason from a completion3 / 5
  1. 01
  2. 02
  3. 04
  4. 05

Read the stop reason from a completion

Example on GitHub(packages/sdk/examples/completion-stop-reason.ts)

Now that we know how to iterate the event stream, let's look at what we get when the stream ends.

Every CompletionFinal carries a stopReason that explains how the model stopped generating:

  • undefined, natural end of sequence (EOS). The model finished on its own. This is the common case.
  • "length", the predict token budget was exhausted. Output is truncated, the model did not reach a natural stopping point.
  • "cancelled", the request was cancelled via cancel({ requestId }).

Setting predict: 10 forces the truncated path. You would call it in the following way:

const result = completion({
  modelId,
  history: [{ role: "user", content: "Say hi in one word." }],
  captureThinking: true,
  generationParams: { predict: 10 },
  stream: true,
});

The drain is necessary even when we only care about final. Note that without it, the stream backs up and result.final never resolves. You would write the drain like so:

for await (const token of result.tokenStream) process.stdout.write(token);

After the drain, we read the aggregate. result.final is the canonical surface for it. Note that the await is what fetches the aggregated contentText, thinkingText, toolCalls, stats, and stopReason:

const final = await result.final;

Branching on stopReason === "length" is how we surface the truncation. Note that the only "length" value here is the budget-truncation path; "cancelled" is a separate branch. Consider the following example:

if (final.stopReason === "length") {
  console.log("▸ truncated: model hit the token budget");
}

Note: a tight predict budget on a short prompt is the easiest way to see the truncation path. In production you usually want a generous budget and rely on EOS, but reading stopReason is how you tell the two apart after the fact.

Put it to the test

  1. Use captureThinking: true and generationParams: { predict: 10 } so the response truncates before it finishes and the model's thinking stays out of the output.
  2. Iterate result.tokenStream to completion.
  3. After the loop, await result.final and read final.stopReason.
  4. Branch on final.stopReason. For "length", log that the token budget cut off the response.
index.ts

$ Run your code to see results

$