# Output Dev LLM Streaming

> Implement LLM text streaming in Output workflow steps with generateTextWithStreaming, Agent.generateWithStreaming, streamText, or Agent.stream. Use when adding token progress, onChunk callbacks, or handling streamText onEnd/onError with Temporal retries.

- Skill: `growthxai/output-dev-llm-streaming` (Agent Skill)
- Install (CLI): `npx skillmds@latest add growthxai/output-dev-llm-streaming`
- Raw SKILL.md: https://api.skillmd.com/api/skills/growthxai/output-dev-llm-streaming/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: growthxai (https://skillmd.com/u/growthxai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/growthxai/output-dev-llm-streaming

---


# LLM Text Streaming

## When to Use This Skill

- Adding token or chunk progress to an LLM-powered step
- Choosing between completed generation and direct stream access
- Using `onChunk`, or `onEnd` / `onError` on `streamText()` / `Agent.stream()`
- Making stream failures trigger Temporal activity retries
- Streaming Agent responses or persisting streamed conversations

## Choose the API

| Need | Use |
|------|-----|
| Complete single-shot result | `generateText()` |
| Complete result plus `onChunk` progress | `generateTextWithStreaming()` |
| Direct access to `textStream` or `stream` | `streamText()` |
| Complete Agent result plus `onChunk` progress | `Agent.generateWithStreaming()` |
| Direct access to the Agent stream | `Agent.stream()` |

In workflow steps, prefer `generateTextWithStreaming()` or `Agent.generateWithStreaming()` when `onChunk` progress is sufficient. They consume the stream internally, return complete results like `generateText()` or `Agent.generate()`, and reject on provider, transport, or abort errors. Rejection allows Temporal to record the failed activity attempt and apply the step retry policy.

`streamText()` and `Agent.stream()` remain supported for code that needs direct control over stream consumption.

## generateTextWithStreaming()

```typescript
import { generateTextWithStreaming } from '@outputai/llm';

const result = await generateTextWithStreaming( {
  prompt: 'draft@v1',
  variables: { topic },
  onChunk( { chunk } ) {
    if ( chunk.type === 'text-delta' ) {
      process.stdout.write( chunk.text );
    }
  }
} );

return result.result;
```

The result has the same complete response fields as `generateText()`, including `result`, `text`, `output`, `usage`, `finishReason`, and `cost`. Structured output passed with `aiSdk.Output.*` is available through `result.output`.

## Agent.generateWithStreaming()

```typescript
const result = await agent.generateWithStreaming( {
  onChunk( { chunk } ) {
    if ( chunk.type === 'text-delta' ) {
      process.stdout.write( chunk.text );
    }
  }
} );
```

`generateWithStreaming()` returns a complete Agent response and automatically stores messages when the Agent has a `messageStore`.

## Direct stream error handling

AI SDK streaming delivers provider and transport failures through `onError`. Iterating `textStream` does not reliably throw the original error. When using `streamText()` in a workflow step, capture the error and throw it after consumption:

```typescript
import { streamText } from '@outputai/llm';

const captured: { error: unknown } = { error: null };
const result = streamText( {
  prompt: 'draft@v1',
  variables: { topic },
  onError( { error } ) {
    captured.error = error;
  }
} );

const chunks: string[] = [];
for await ( const chunk of result.textStream ) {
  chunks.push( chunk );
}

if ( captured.error ) {
  throw captured.error;
}

return chunks.join( '' );
```

Registering `onError` without throwing the captured error can let the step return an empty successful result, preventing Temporal from retrying it. Awaiting a completion property may also produce a generic no-output error instead of the original provider error.

`Agent.stream()` stores conversation messages in its wrapped `onEnd` when `finishReason` is not `'error'`. Use `Agent.generateWithStreaming()` when a complete stored response meets the requirement.

Streaming call arguments: `prompt`, `promptDir`, `variables`, `tools`, `output`, `toolChoice`, `stopWhen`, `abortSignal`, plus `onChunk` (`generateTextWithStreaming`) or `onChunk` / `onEnd` / `onError` (`streamText`). Agent methods: `messages`, `abortSignal`, `toolChoice`, plus those same stream callbacks.

## Rules

- Prefer the completed streaming APIs in Temporal steps unless direct stream access is required.
- Do not rely on `onError` alone to fail a step using `streamText()`.
- Throw the captured error only after stream consumption finishes.
- Keep `onChunk` side effects bounded. A Temporal signal per token creates a history event per signal, so batch high-frequency updates.
- Do not describe `streamText()` or `Agent.stream()` as deprecated.

## Related Skills

- `output-dev-step-function` - Put LLM calls inside Temporal activity steps
- `output-dev-agent-class` - Construct and use reusable Agents
- `output-dev-prompt-file` - Create prompt files for generation
- `output-error-try-catch` - Handle step and workflow failures

