Custom Data Parts: ID Reconciliation
Überblick
Abschnitt betitelt „Überblick“Wenn Du ein Data Part mit einer ID sendest und später ein weiteres Data Part mit derselben ID, wird das bestehende Part aktualisiert — nicht ein neues angehaengt. Das ermoeglicht progressive Loading States: erst ein Skeleton anzeigen, dann mit echten Daten ersetzen.
Ohne ID-Reconciliation wuerdest Du für jeden Status-Wechsel einen neuen Eintrag in message.parts bekommen. Mit IDs bleibt es ein einziger Eintrag, der sich vom Loading-State zum fertigen Ergebnis entwickelt. Das haelt die UI sauber und die Message-Historie kompakt.
Dieses Pattern ist besonders nuetzlich für Tool-Ergebnisse, die asynchron geladen werden — z.B. eine Wetter-Abfrage, die erst “Lade…” zeigt und dann die Temperatur.
Server: Progressive Updates senden
Abschnitt betitelt „Server: Progressive Updates senden“import { createUIMessageStreamResponse, streamText } from 'ai';
export async function POST(req: Request) { const { messages } = await req.json();
return createUIMessageStreamResponse({ execute: async (writer) => { // Loading State senden writer.write({ type: 'data-weather', // ← Custom Type mit data- Prefix id: 'weather-berlin', // ← ID setzen data: { city: 'Berlin', status: 'loading' }, });
const result = streamText({ model, messages });
// LLM-Stream in den Writer mergen writer.merge(result.toUIMessageStream());
// Später: Update mit echten Daten (gleiche ID) const weather = await fetchWeather('Berlin'); writer.write({ type: 'data-weather', // ← Gleicher Type id: 'weather-berlin', // ← Gleiche ID → UPDATE data: { city: 'Berlin', status: 'success', temp: weather.temp, }, }); }, });}Frontend: Automatisches Update
Abschnitt betitelt „Frontend: Automatisches Update“// In message.parts gibt es nur EINEN Eintrag für 'weather-berlin'// — den jeweils aktuellsten Stand
{m.parts.map((part, i) => { if (part.type === 'data-weather') { return part.data.status === 'loading' ? <WeatherSkeleton key={i} city={part.data.city} /> : <WeatherCard key={i} data={part.data} />; } return null;})}Verhalten
Abschnitt betitelt „Verhalten“| Szenario | Ohne ID | Mit ID |
|---|---|---|
| Zweites Data Part gesendet | Neuer Eintrag in parts | Bestehender Eintrag aktualisiert |
parts.length | Waechst mit jedem Send | Bleibt gleich (pro ID) |
| UI-Rendering | Mehrere Elemente (alt + neu) | Ein Element (aktueller Stand) |
| Loading → Success | Zwei sichtbare Elemente | Ein Element, das sich ändert |
Mehrere parallele Updates
Abschnitt betitelt „Mehrere parallele Updates“// Drei unabhängige Data Parts mit eigenen IDswriter.write({ type: 'data-weather', id: 'weather-berlin', data: { city: 'Berlin', status: 'loading' },});writer.write({ type: 'data-weather', id: 'weather-muenchen', data: { city: 'Muenchen', status: 'loading' },});writer.write({ type: 'data-stock', id: 'stock-price', data: { symbol: 'AAPL', status: 'loading' },});
// Jedes wird unabhängig aktualisiert, sobald die Daten da sindSiehe auch
Abschnitt betitelt „Siehe auch“- Challenge 7.1: Custom Data Parts — Hands-on mit Data Parts
- Custom Data Parts streamen — Server-seitige Erstellung
- Custom Data Parts im Frontend — Frontend-Konsum