import { useAgent } from "agents/react"; import { useState } from "react"; import { Button, Input, Surface, Text } from "@cloudflare/kumo"; import { DemoWrapper } from "../../layout"; import { LogPanel, ConnectionStatus, CodeExplanation, HighlightedCode, HighlightedJson, type CodeSection } from "../../components"; import { useLogs, useUserId, useToast } from "../../hooks"; import type { StreamingAgent } from "./streaming-agent"; const codeSections: CodeSection[] = [ { title: "Define a streaming method", description: "Add streaming: true to the @callable decorator. The method receives a StreamingResponse as its first argument — call stream.send() to push chunks and stream.end() to finish.", code: `import { Agent, callable, type StreamingResponse } from "agents"; class StreamingAgent extends Agent { @callable({ streaming: true }) async countdown(stream: StreamingResponse, from: number) { for (let i = from; i >= 0; i--) { stream.send({ count: i, label: i === 0 ? "Liftoff!" : \`\${i}...\` }); await new Promise(r => setTimeout(r, 500)); } stream.end({ launched: true }); } }` }, { title: "Consume the stream on the client", description: "Pass onChunk, onDone, and onError callbacks as the third argument to agent.call(). Chunks arrive as they are sent from the server.", code: `await agent.call("countdown", [5], { onChunk: (chunk) => { // { count: 4, label: "4..." } console.log(chunk); }, onDone: (final) => { // { launched: true } console.log("Stream complete:", final); }, onError: (error) => { console.error("Stream error:", error); }, });` } ]; export function StreamingDemo() { const userId = useUserId(); const { logs, addLog, clearLogs } = useLogs(); const { toast } = useToast(); const [chunks, setChunks] = useState([]); const [finalResult, setFinalResult] = useState(null); const [isStreaming, setIsStreaming] = useState(false); const [count, setCount] = useState("10"); const [countdown, setCountdown] = useState("5"); const [errorAfter, setErrorAfter] = useState("3"); const agent = useAgent({ agent: "streaming-agent", name: `streaming-demo-${userId}`, onOpen: () => addLog("info", "connected"), onClose: () => addLog("info", "disconnected"), onError: () => addLog("error", "error", "Connection error") }); const handleStream = async (method: string, args: unknown[]) => { setChunks([]); setFinalResult(null); setIsStreaming(true); addLog("out", "stream_start", { method, args }); try { await ( agent.call as ( m: string, a?: unknown[], opts?: unknown ) => Promise )(method, args, { onChunk: (chunk: unknown) => { addLog("in", "chunk", chunk); setChunks((prev) => [...prev, chunk]); }, onDone: (final: unknown) => { addLog("in", "stream_done", final); setFinalResult(final); setIsStreaming(false); toast("Stream complete — " + chunks.length + " chunks", "success"); }, onError: (error: string) => { addLog("error", "stream_error", error); setIsStreaming(false); toast("Stream error", "error"); } }); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); setIsStreaming(false); toast(e instanceof Error ? e.message : String(e), "error"); } }; return ( Add{" "} streaming: true {" "} to any{" "} @callable {" "} decorator to stream data chunk-by-chunk from server to client. The method receives a{" "} StreamingResponse {" "} object — call{" "} stream.send() {" "} to push data and{" "} stream.end() {" "} to finish. Try the countdown to see streaming with delays. } statusIndicator={ } >
{/* Controls */}
{/* Stream Numbers */}
Stream Numbers

Streams numbers from 1 to N synchronously

) => setCount(e.target.value) } className="w-20" min={1} max={100} />
{/* Countdown */}
Countdown

Streams a countdown with 500ms delays between numbers

) => setCountdown(e.target.value) } className="w-20" min={1} max={20} />
{/* Stream with Error */}
Stream with Error

Sends N chunks then errors (tests error handling mid-stream)

) => setErrorAfter(e.target.value) } className="w-20" min={1} max={10} />
{/* Stream Output */}
Stream Output {isStreaming && ( receiving... )}
Chunks ({chunks.length}) {chunks.length === 0 ? (

No chunks yet

) : ( JSON.stringify(c)).join("\n")} lang="json" /> )}
{finalResult !== null && (
Final Result
)}
{/* Logs */}
); }