Zum Inhalt springen
EN DE

Boss Fight: Real-Time Dashboard

Du baust ein Real-Time Dashboard — ein Streaming-System, das einem User in Echtzeit eine Analyse liefert. Der Stream transportiert neben dem Analyse-Text auch strukturierte Status-Updates, nutzt Message Metadata für Session-Tracking, glaettet die Ausgabe und faengt Fehler graceful ab.

Dein Dashboard soll sich so anfuehlen:

[Status: searching] Recherchiere Daten...
[Fortschritt: 1/3] Marktdaten geladen.
Die Analyse zeigt folgende Trends: Im ersten Quartal stieg
der Umsatz um 12%. Der wichtigste Treiber war...
[Fortschritt: 2/3] Wettbewerbsanalyse abgeschlossen.
Im Vergleich zum Wettbewerb liegt das Unternehmen bei...
[Fortschritt: 3/3] Prognose berechnet.
[Status: done] Analyse abgeschlossen.
[Session: user-42 | Dauer: 4.2s | Tokens: 847]

Dieses Projekt verbindet alle vier Bausteine:

Boss Fight Architektur: User Query mit Metadata fließt über API Route zu createDataStream und streamText mit Transforms und Error Handler ins Dashboard UI
  1. Custom Data Parts (Challenge 7.1) — Sende strukturierte Status-Updates im Stream: “searching”, “analyzing”, “done”. Sende mindestens 3 Fortschritts-Updates mit aktuellem Schritt und Gesamtzahl (z.B. { step: 1, total: 3, label: "Marktdaten geladen" }).

  2. Message Metadata (Challenge 7.2) — Jede User-Message traegt Metadata: userId, timestamp, sessionId. Die Assistant-Antwort traegt Metadata: modelId, generatedAt, tokenCount. Logge die User-Metadata serverseitig, BEVOR der LLM-Call startet.

  3. Stream Transforms (Challenge 7.3) — Nutze smoothStream() für fluessige Textausgabe. Schreibe einen Custom Transform, der vertrauliche Daten maskiert (z.B. E-Mail-Adressen durch “[REDACTED]” ersetzt).

  4. Error Handling (Challenge 7.4) — Implementiere onError in streamText für serverseitiges Logging. Implementiere onError in toUIMessageStreamResponse für User-freundliche Fehlermeldungen. Wrapp den Stream-Consumer in try/catch als letzten Fallback.

  5. Session-Statistik — Am Ende des Streams: Berechne die Dauer (aus Metadata-Timestamps) und sende eine finale Statistik als Data Part: { type: 'stats', duration, tokenCount, userId }.

import { createDataStream, smoothStream, streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
// TODO: Message mit Metadata erstellen
// TODO: Custom Transform für Daten-Maskierung
// TODO: createDataStream mit Status-Updates
// TODO: streamText mit smoothStream + Custom Transform
// TODO: Error Handling (onError + try/catch)
// TODO: Session-Statistik am Ende

Dein Boss Fight ist bestanden, wenn:

  • Mindestens 3 Custom Data Parts mit Fortschritts-Updates werden im Stream gesendet
  • User-Messages tragen Metadata (userId, timestamp, sessionId) die das LLM nicht sieht
  • Die Assistant-Antwort traegt Metadata (modelId, generatedAt)
  • smoothStream() ist als Transform konfiguriert
  • Ein Custom Transform maskiert mindestens ein Datenmuster (E-Mail, Telefonnummer, o.ae.)
  • onError ist sowohl in streamText als auch in toUIMessageStreamResponse implementiert
  • Ein try/catch um den Stream-Consumer faengt unerwartete Fehler ab
  • Am Ende wird eine Session-Statistik als Data Part gesendet (Dauer + Tokens)
Hinweis 1: createDataStream Struktur

createDataStream erwartet eine execute Funktion, die den dataStream Controller als Parameter bekommt. In dieser Funktion rufst Du dataStream.writeData() für Custom Data Parts auf und result.mergeIntoDataStream(dataStream) für den LLM-Stream. Die Reihenfolge bestimmt, wann Data Parts im Stream erscheinen.

Hinweis 2: Timing für Fortschritts-Updates

Du kannst setTimeout oder setInterval nutzen, um Fortschritts-Updates zeitversetzt zu senden. Oder nutze die onStepFinish bzw. onChunk Callbacks von streamText, um Data Parts basierend auf dem Stream-Fortschritt zu senden. Wichtig: Beende Intervalle in onFinish, sonst laeuft der Stream nie zu Ende.

Hinweis 3: Custom Transform + smoothStream kombinieren

experimental_transform akzeptiert ein Array. smoothStream() sollte zuerst kommen (glaettet den Raw-Stream), danach Dein Custom Transform (arbeitet mit den geglätteten Chunks). Denke daran: Dein Transform muss text-delta Chunks modifizieren und alle anderen Typen unveraendert durchlassen.

Hinweis 4: Session-Dauer berechnen

Speichere Date.now() am Anfang des execute Callbacks. In onFinish von streamText berechnest Du die Differenz. Sende das Ergebnis als Data Part: dataStream.writeData({ type: 'stats', durationMs: Date.now() - startTime, ... }).

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