"use client";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
function App() {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({ api: "/api/chat" }),
});
return (
<AssistantRuntimeProvider runtime={runtime}>
{/* Your chat UI */}
</AssistantRuntimeProvider>
);
}import { streamText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
import { serializableMediaCardSchema } from "@/components/tool-ui/media-card/schema";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages,
tools: {
previewLink: tool({
description: "Show a preview card for a URL",
inputSchema: z.object({ url: z.string().url() }),
outputSchema: serializableMediaCardSchema,
async execute({ url }) {
// In production, you'd fetch real metadata here
return {
id: "link-preview-1",
assetId: url,
kind: "link",
href: url,
title: "Example Site",
description: "A description of the linked content",
thumb: "https://example.com/image.jpg",
};
},
}),
},
});
return result.toUIMessageStreamResponse();
}import { makeAssistantToolUI } from "@assistant-ui/react";
import {
MediaCard,
parseSerializableMediaCard,
} from "@/components/tool-ui/media-card";
export const PreviewLinkUI = makeAssistantToolUI({
toolName: "previewLink", // Must match backend tool name
render: ({ result }) => {
const card = parseSerializableMediaCard(result);
return <MediaCard {...card} maxWidth="420px" />;
},
});
// Register in your app
function App() {
return (
<AssistantRuntimeProvider runtime={runtime}>
<PreviewLinkUI />
<Thread />
</AssistantRuntimeProvider>
);
}npx assistant-ui@latest initpnpm add @assistant-ui/react @assistant-ui/react-ai-sdk ai @ai-sdk/openai zodOPENAI_API_KEY=sk-...