assistant-ui/skills

streaming

Streaming wire protocols and backend helpers for assistant-ui, built on the assistant-stream package.

查看源码
仓库原始内容

按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。

assistant-ui Streaming

Always consult [assistant-ui.com/llms.txt](https://www.assistant-ui.com/llms.txt) for the latest API.

assistant-stream is the wire layer underneath assistant-ui's chat runtimes. It normalizes every backend into one stream of AssistantStreamChunk values, ships encoders and decoders for three wire formats, and adds a resumable-stream layer on top of any of them. If your backend already speaks the Vercel AI SDK, you rarely touch this package directly (streamText plus toUIMessageStream is enough); reach for it when you write a custom endpoint, need to decode a stream yourself, or want resumable streams.

References

When to use it

Streaming the model call through the Vercel AI SDK?
├─ Yes → streamText + toUIMessageStream/createUIMessageStreamResponse (or result.toUIMessageStreamResponse())
│        assistant-stream is optional: only needed to decode the response yourself or add resumable streams
└─ No → build the response with assistant-stream
    ├─ Emitting message parts (text, reasoning, tool calls) → Data Stream
    └─ Streaming a full agent state snapshot with custom commands → Assistant Transport

Installation

bash
npm install assistant-stream

@assistant-ui/ai-sdk is the current AI SDK integration package (framework neutral); @assistant-ui/react-ai-sdk still re-exports the same API for older installs but new code should import from @assistant-ui/ai-sdk.

Build a custom streaming response

createAssistantStreamResponse runs a callback with an AssistantStreamController and returns a Response encoded as Data Stream (see data-stream.md for the alternative encoders).

ts
import { createAssistantStreamResponse } from "assistant-stream";

export async function POST(req: Request) {
  return createAssistantStreamResponse(async (controller) => {
    controller.appendText("Hello ");
    controller.appendText("world!");

    controller.appendReasoning("Checking the forecast first.", {
      unstable_summary: "Looking up the weather",
    });

    controller.appendSource({
      type: "source",
      sourceType: "url",
      id: "s1",
      url: "https://example.com/forecast",
      title: "Forecast",
    });

    const tool = controller.addToolCallPart({ toolName: "get_weather" });
    tool.argsText.append('{"city":"NYC"}');
    tool.argsText.close();
    tool.setResponse({ result: { temperature: 22 } });

    controller.close();
  });
}

close() closes any part still open and ends the stream; an uncaught throw inside the callback is turned into an error chunk automatically.

AssistantStreamController

Every server-side stream, whichever encoder ends up wrapping it, is written through this controller (createAssistantStream, createAssistantStreamController, and createAssistantStreamResponse all hand you one).

MethodSignatureNotes
appendText(textDelta: string) => voidOpens a text part on first call, appends to it on the next
appendReasoning(reasoningDelta: string, options?: { unstable_summary?: string }) => voidPassing options always opens a new part, so a summary lands on a part of its own
appendSource(part: SourcePart) => voidSourcePart is { type: "source", sourceType: "url", id, url, title?, parentId? }
appendFile(part: FilePart) => voidFilePart is { type: "file", data, mimeType, parentId? }
appendData(part: DataPart) => voidDataPart is { type: "data", name, data, parentId? }, a named app-defined part
addTextPart() => TextStreamControllerExplicit { append(text), close() } writer, for interleaving with other parts
addReasoningPart(options?) => TextStreamControllerSame writer shape as addTextPart
addToolCallPart(toolName: string) => ToolCallStreamControllerGenerates a toolCallId; see the object overload below for a stable id
addToolCallPart(init: ToolCallPartInit) => ToolCallStreamController{ toolCallId?, toolName, argsText?, args?, response? }
enqueue(chunk: AssistantStreamChunk) => voidRaw escape hatch; prefer the helpers above
merge(stream: AssistantStream) => voidSplices another AssistantStream's parts into this one
withParentId(parentId: string) => AssistantStreamControllerReturns a controller whose writes attach parentId (nested or related parts)
close() => voidCloses the open part, then the stream

addToolCallPart returns a ToolCallStreamController: { argsText: TextStreamController, setResponse(response), close() }. setResponse takes { result, artifact?, isError?, modelContent?, messages? } (the shape returned by a ToolResponse), closes the part automatically, and ignores a second call.

Stream events and part types

Every decoder, regardless of wire format, yields the same normalized AssistantStreamChunk union ({ path: number[] } & { type, ... }):

typeExtra fields
part-startpart: PartInit (see below)
part-finishnone
tool-call-args-text-finishnone
text-deltatextDelta: string
annotationsannotations: ReadonlyJSONValue[]
datadata: ReadonlyJSONValue[]
step-startmessageId: string
step-finishfinishReason, usage: { inputTokens, outputTokens }, isContinued: boolean
message-finishfinishReason, usage
resultresult, isError: boolean, artifact?, modelContent?, messages?
error`error: string, code?, severity?: "critical" \"warning" \"info"`
update-stateoperations: AssistantTransportStateOperation[] (see assistant-transport.md)

PartInit (the part field of part-start) is one of six part types, every variant carrying an optional parentId:

typeExtra fields
textnone
reasoningunstable_summary?: string
tool-calltoolCallId: string, toolName: string
sourcesourceType: "url", id, url, title?
filedata: string, mimeType: string
dataname: string, data: ReadonlyJSONValue

Common Gotchas

`appendSource`, `appendFile`, or `appendData` silently drops the part

  • Pass the full part object including its type field ("source", "file", or "data"); the method name does not imply it for you.

A tool call never settles in the UI

  • addToolCallPart needs a toolName; the id is generated for you unless you pass one. Close argsText (or call setResponse, which closes it for you) or the part never finishes. Register the rendering with a "use generative" toolkit, not the deprecated makeAssistantToolUI; see tools.

Two separate reasoning parts merge into one on the client

  • On the Data Stream wire, a reasoning part-start frame is only sent when unstable_summary is set; a plain appendReasoning(text) call travels only as text deltas, and the decoder has nothing else to tell it a new part started. Opening two summary-less reasoning parts back to back (for example around a tool call) reconstructs as one continuous reasoning part on the client. Give each part a unstable_summary (even an empty-feeling one) or route the tool call through a separate message step to keep them distinct.

Stream not updating the UI

  • Check the Content-Type against the encoder you actually used: DataStreamEncoder (the createAssistantStreamResponse default) sends text/plain; charset=utf-8 with x-vercel-ai-data-stream: v1, not text/event-stream. AssistantTransportEncoder and the AI SDK's UI message stream do send text/event-stream.

Decoder throws "Stream ended abruptly without receiving [DONE] marker"

  • AssistantTransportDecoder and UIMessageStreamDecoder require the terminal [DONE] sentinel; a proxy, CDN, or middleware that buffers or truncates the body breaks this. DataStreamDecoder has no such marker.

`createAssistantStreamResponse` always encodes as Data Stream

  • It hard-codes DataStreamEncoder. For a different wire format, encode manually: AssistantStream.toResponse(createAssistantStream(callback), new AssistantTransportEncoder()), or use createAssistantStreamController and encode the returned stream yourself.

Related Skills

  • runtime -- useLocalRuntime, useExternalStoreRuntime, and the useAssistantTransportRuntime React hook and state hooks
  • setup -- scaffolding an AI SDK route handler and useChatRuntime
  • tools -- "use generative" toolkits and tool-call rendering
  • cloud -- persisting streamed threads and messages with assistant-cloud
来自同一仓库

更多 Skills

全部 Skills
assistant-ui
社区

assistant-ui

Overview and router for assistant-ui, the React library for building AI chat interfaces from composable primitives and a styled elements catalog. Use for high-level, cross-cutting, or architecture questions: choosing packages, picking a runtime, or understanding the layers (elements, primitives, the aui client with AuiConfig and AuiProvider, the runtime, adapters) and the message model. Covers @assistant-ui/react 0.15.x, the framework-neutral @assistant-ui/ai-sdk integration for AI SDK v7 (useChatRuntime, AssistantChatTransport; @assistant-ui/react-ai-sdk re-exports it), @assistant-ui/core, @assistant-ui/store, assistant-stream, assistant-cloud, the adapters for LangGraph, LangChain, Google ADK, A2A, AG-UI, Eve, OpenCode, and Pi, and the platform bindings @assistant-ui/react-native and @assistant-ui/react-ink; AssistantRuntimeProvider; the primitives ThreadPrimitive, MessagePrimitive, ComposerPrimitive; the hooks useAui, useAuiState, useAuiEvent; and runtime selection across useChatRuntime, useExternalStoreRuntime, useLangGraphRuntime, useLocalRuntime. For a specific area route to a focused sibling instead: setup, elements, primitives, runtime, tools, generative-ui, streaming, cloud, thread-list, copilots, markdown, react-mcp, observability, react-native, ink, or update.

安装量
1
GitHub Stars
26
最近更新
9月4日
assistant-ui
社区

cloud

Adds AssistantCloud backed persistence, authorization, and telemetry to assistant-ui apps. Use when wiring cross-session thread and message history, multi-device chat, message feedback, file uploads, or auth: passing cloud to useChatRuntime from @assistant-ui/ai-sdk, AISDKThreads({ cloud }) for AuiConfig hosts, the standalone useCloudChat/useThreads hooks from @assistant-ui/cloud-ai-sdk, or cloud on useLangGraphRuntime. Covers constructing AssistantCloud with authToken (JWT), apiKey plus userId/workspaceId (server-side), or anonymous; direct provider integrations (Clerk, Auth0, Supabase, Firebase) and a backend token endpoint; and the client surface verified against source: cloud.threads.{list,get,create,update,delete}, cloud.threads.messages.{list,create,update,feedback}, cloud.files.{generatePresignedUploadUrl,pdfToImages}, cloud.runs.{stream,report}, cloud.projects.threads, cloud.auth.tokens.create, and cloud.telemetry. Also covers a custom ThreadHistoryAdapter built on CloudMessagePersistence/createFormattedPersistence, run telemetry (beforeReport, sub-agent tracking with wrapSamplingHandler), and the NEXTPUBLICASSISTANTBASEURL/ASSISTANTAPIKEY env vars. Route here for threads that do not persist, 401s against the cloud API, or feedback buttons that do not save. For the sidebar UI itself use thread-list; for the general RemoteThreadListAdapter/ThreadHistoryAdapter contract use runtime.

安装量
1
GitHub Stars
26
最近更新
9月4日
assistant-ui
社区

copilots

Grounding an assistant in your app with assistant-ui copilots (@assistant-ui/react). Use when steering assistant behavior with useAssistantInstructions, feeding lazy send-time app state through useAssistantContext({ getContext }), exposing rendered components to the assistant with makeAssistantVisible(Component, { clickable, editable }), giving the model two-way component state through interactables (unstableuseInteractable for app-scoped panels, unstableinteractableTool inside defineToolkit for thread-scoped artifacts, both mounted via AuiConfig({ unstableinteractables: unstableInteractables() })), registering instructions and tools together imperatively with aui.modelContext.register({ getModelContext }), or bridging model context across an iframe boundary with AssistantFrameProvider and useAssistantFrameHost. The legacy Interactables() scope, useAssistantInteractable, and useInteractableState are deprecated since 2026-06-14 and scheduled for removal on or after 2026-09-14; new code uses the unstable API. Reach for this when the assistant should read the current page, click or edit UI, read and update component state through auto-generated update{name} tools, or receive tools and instructions from a sandboxed iframe. For LLM tools and tool-call UI use the tools skill; for runtime and thread state use the runtime skill.

安装量
1
GitHub Stars
26
最近更新
9月4日
assistant-ui
社区

elements

Installs and customizes assistant-ui elements, the styled shadcn-style component catalog at assistant-ui.com/elements served from the r.assistant-ui.com registry through npx assistant-ui@latest add . Use when adding a prebuilt chat surface or widget (Thread, ThreadList, AssistantModal, AssistantSidebar, ToolFallback, ToolGroup, MarkdownText, Reasoning, Sources, Attachment, ModelSelector, Voice orb, McpConfig) or one of the 120 standalone elements (approval card, agent plan, code diff, data table, chart, trace waterfall, message queue, composer variants, and so on), choosing between runtime-connected .aui.tsx files and props-driven standalone files, overriding Thread slots through the components prop, editing the copied source under components/assistant-ui/elements/, using the shared surfaces.tsx tokens, or picking the Radix versus Base UI flavor through the style-aware registry URL in components.json. Route here when an import from @/components/assistant-ui/... fails, an element renders unstyled, or the CLI installs the wrong flavor. For unstyled building blocks use primitives; for the CLI scaffold itself use setup.

安装量
1
GitHub Stars
26
最近更新
9月4日