Zum Inhalt springen
EN DE

Text Parts manuell streamen

Normalerweise uebernimmt streamText das Schreiben von Text Parts automatisch. Aber manchmal brauchst Du mehr Kontrolle: mehrere LLM-Outputs kombinieren, eigenen Text einfuegen oder Text aus Nicht-LLM-Quellen streamen. In diesen Faellen schreibst Du Text Parts manuell über writer.write().

Ein Text-Part in der UI durchlaeuft einen Lifecycle: text-start oeffnet einen neuen Text-Part, dann folgen beliebig viele text-delta Parts mit den eigentlichen Text-Fragmenten, und text-end schliesst den Text-Part ab. Wenn Du writer.merge() verwendest, sendet streamText diese Lifecycle-Events automatisch. Bei rein manuellem Schreiben über writer.write() genuegt es, text-delta Parts zu senden — der Stream kuemmert sich um Start und Ende.

Du kannst manuelle Text-Deltas mit writer.merge() kombinieren — eigener Text vor, nach oder zwischen LLM-Streams.

Dieses Pattern ist besonders nuetzlich für Workflow-Szenarien, in denen mehrere LLM-Calls nacheinander ausgefuehrt werden und Du Ueberschriften oder Trennzeichen dazwischen einfuegen willst.

import { createUIMessageStreamResponse } from 'ai';
export async function POST(req: Request) {
return createUIMessageStreamResponse({
execute(writer) {
// Manuell Text Parts schreiben
writer.write({ type: 'text-delta', textDelta: 'Hello ' });
writer.write({ type: 'text-delta', textDelta: 'World!' });
},
});
}
import { createUIMessageStreamResponse, streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
export async function POST(req: Request) {
const { messages } = await req.json();
return createUIMessageStreamResponse({
async execute(writer) {
// Eigener Text vor dem LLM-Stream
writer.write({ type: 'text-delta', textDelta: '## Antwort\n\n' });
const result = streamText({
model: anthropic('claude-sonnet-4-5-20250514'),
messages,
});
writer.merge(result.toUIMessageStream());
await result;
// Eigener Text nach dem LLM-Stream
writer.write({
type: 'text-delta',
textDelta: '\n\n---\nGeneriert mit AI SDK',
});
},
});
}
import { createUIMessageStreamResponse, streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
export async function POST(req: Request) {
return createUIMessageStreamResponse({
async execute(writer) {
// Abschnitt 1: Zusammenfassung
writer.write({ type: 'text-delta', textDelta: '## Zusammenfassung\n\n' });
const summary = streamText({
model: anthropic('claude-sonnet-4-5-20250514'),
prompt: 'Fasse zusammen: ...',
});
writer.merge(summary.toUIMessageStream());
await summary;
// Abschnitt 2: Analyse
writer.write({ type: 'text-delta', textDelta: '\n\n## Analyse\n\n' });
const analysis = streamText({
model: anthropic('claude-sonnet-4-5-20250514'),
prompt: 'Analysiere: ...',
});
writer.merge(analysis.toUIMessageStream());
await analysis;
// Abschluss
writer.write({ type: 'text-delta', textDelta: '\n\n---\nFertig.' });
},
});
}

Wenn streamText über writer.merge() eingebunden wird, sendet es automatisch die vollständige Sequenz:

  1. text-start — Oeffnet einen neuen Text-Part
  2. text-delta (n-mal) — Sendet Text-Fragmente inkrementell
  3. text-end — Schliesst den Text-Part ab

Bei manuellen writer.write()-Aufrufen mit text-delta wird der Lifecycle ebenfalls korrekt gehandhabt.

Stream PartPropertiesBeschreibung
text-startOeffnet einen neuen Text-Part
text-deltatextDelta: stringTextfragment, wird an aktuellen Text-Part angehaengt
text-endSchliesst den aktuellen Text-Part ab
step-startmessageId, request, warningsMarkiert den Beginn eines neuen Schritts
step-finishfinishReason, usage, isContinuedMarkiert das Ende eines Schritts
MethodeBeschreibung
writer.write({ type, ... })Schreibt einen einzelnen Stream Part
writer.merge(stream)Fuegt einen externen Stream (z.B. result.toUIMessageStream()) ein

Part of AI Learning — free courses from prompt to production. Jan on LinkedIn