@vtex/agentic-ui
v0.4.6
Published
A comprehensive React/Next.js library for building AI agent interfaces with chat, canvas, and interactive components integrated with VTEX Raccoon.
Maintainers
Keywords
Readme
@vtex/agentic-ui
A comprehensive React/Next.js library for building AI agent interfaces with chat, canvas, and interactive components integrated with VTEX Raccoon.
Overview
@vtex/agentic-ui provides a complete toolkit for creating an AI agent user interfaces within the VTEX ecosystem. It offers a standardized way to build conversational interfaces, display rich content, manage state, and integrate seamlessly with VTEX Admin applications.
Main Features
- React Components - Pre-built components for chat, canvas, messages, threads, and tools
- Composable Architecture - MessageComposer and other components use composition pattern for maximum flexibility
- Agent Integration System -
useAgentPlatformhook andcreateAgent()function for type-safe agent configuration - UI Protocol - Standardized communication layer between frontend and backend with automatic authentication
- SSE Integration - Server-Sent Events with automatic reconnection, backoff, and connection management
- State Management - Built-in hooks and utilities powered by Jotai
- VTEX Admin Integration - Drop-in solution for admin application development with route-based agent loading
- Tool Registry - Extensible system for custom tool rendering with canvas and inline modes
- TypeScript Support - Full type safety across the entire library with Zod schema validation
Installation
pnpm add @vtex/agentic-ui@latestPeer Dependencies
This package requires the following peer dependencies:
react>= 18.3react-dom>= 18.3next>= 14@vtex/shoreline(latest)@vtex/raccoon-next(latest)@vtex/raccoon-analytics(latest)@tanstack/react-table^8.21.3typescript>= 5
Documentation
| Guide | Description | |-------|-------------| | Components | Complete reference for all React components, hooks, and utilities | | UI Protocol | Communication protocol specification and data fetching hooks | | Server-Sent Events (SSE) | Real-time communication utilities with automatic reconnection | | Admin Integration | Integration solution for VTEX Admin applications | | Migration Guide | Step-by-step guide for migrating from 0.2.4 to 0.4.1 |
Quick Start
This example demonstrates how to set up a Next.js application with Raccoon and agentic-ui.
Step 1: Setup Application Root
Create your _app.tsx file with the necessary providers:
import type { AppProps } from "next/app";
import { useRouter } from "next/router";
import { connect, bootstrap, useAdmin } from "@vtex/raccoon-next";
import {
ChatProvider,
useAgentPlatform,
AgenticUIProvider,
} from "@vtex/agentic-ui";
import { IntlProvider } from "react-intl";
// Import required styles
import "@vtex/shoreline/css";
import "@vtex/agentic-ui/css";
import { agents } from "../agents";
import { messages } from "../messages";
// Initialize Raccoon connection
connect();
function AppContent({ Component, pageProps }: AppProps) {
const router = useRouter();
// Load agent configuration based on current route
const { isLoading, agent, error } = useAgentPlatform({
agents,
pathname: router.asPath,
});
const { locale = "en-US", account, token } = useAdmin();
if (isLoading) {
return <div>Loading...</div>;
}
if (error || !agent) {
return <div>Failed to load agent</div>;
}
return (
<AgenticUIProvider
agent={agent}
locale={locale}
account={account}
token={token}
>
<IntlProvider
locale={locale}
messages={
messages[locale as keyof typeof messages] ?? messages["en-US"]
}
defaultLocale="en-US"
>
<ChatProvider>
<Component {...pageProps} />
</ChatProvider>
</IntlProvider>
</AgenticUIProvider>
);
}
function App(props: AppProps) {
return <AppContent {...props} />;
}
export default bootstrap(App);Step 2: Create an Agent Home Page
Create a page component that renders the initial chat interface at pages/app/[app]/index.tsx:
import { useState } from "react";
import { useIntl } from "react-intl";
import { useNavigation } from "@vtex/raccoon-next";
import {
AgentHome,
ChatLayout,
MessageComposer,
MessageComposerInput,
MessageComposerTextarea,
MessageComposerControls,
MessageComposerSendButton,
MessageComposerSuggestions,
Suggestion,
useAgenticUI,
useChatContext,
useResetAtoms,
useStatus,
} from "@vtex/agentic-ui";
export default function AgenticPage() {
const { reset } = useResetAtoms();
const { agent } = useAgenticUI();
const { formatMessage } = useIntl();
const { stop, sendMessage, appSlug } = useChatContext();
const { loading } = useStatus();
const { navigate } = useNavigation();
const [input, setInput] = useState("");
const basePath = `/app/${appSlug}`;
async function handleSendMessage() {
const userContent = input.trim();
setInput("");
try {
const { threadId } = await sendMessage({
inputs: [{ type: "text", text: userContent }],
});
navigate(`${basePath}/${threadId}`);
} catch (error) {
console.error(error);
}
}
return (
<ChatLayout
type="home"
onThreadClick={(threadId) => {
reset();
navigate(`${basePath}/${threadId}`);
}}
>
<div data-chat-container data-is-empty>
<AgentHome
title={formatMessage({ id: agent?.ui.title })}
subtitle={formatMessage({ id: agent?.ui.subtitle })}
name={formatMessage({ id: agent?.ui.name })}
/>
<MessageComposer
input={input}
setInput={setInput}
isLoading={loading}
isEmpty
onSend={handleSendMessage}
onStop={stop}
>
<MessageComposerInput autoGrow>
<MessageComposerTextarea />
<MessageComposerControls>
<MessageComposerSendButton />
</MessageComposerControls>
</MessageComposerInput>
<MessageComposerSuggestions>
{agent?.ui.promptSuggestions.map((prompt) => (
<Suggestion
key={formatMessage({ id: prompt.title })}
title={formatMessage({ id: prompt.title })}
description={formatMessage({ id: prompt.description })}
promptText={formatMessage({ id: prompt.promptText })}
requiresUrl={prompt.requiresUrl}
requiresImage={prompt.requiresImage}
/>
))}
</MessageComposerSuggestions>
</MessageComposer>
</div>
</ChatLayout>
);
}Step 3: Create a Thread/Conversation Page
Create a page for ongoing conversations at pages/app/[app]/[threadId]/index.tsx:
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { useNavigation } from "@vtex/raccoon-next";
import {
ChatLayout,
MessageComposer,
MessageComposerInput,
MessageComposerTextarea,
MessageComposerControls,
MessageComposerSendButton,
MessageComposerFeedback,
MessagesArea,
useChatContext,
useMessageIds,
useResetAtoms,
useStatus,
useThreadId,
} from "@vtex/agentic-ui";
export default function ThreadPage() {
const { stop, sendMessage, appSlug } = useChatContext();
const { navigate } = useNavigation();
const router = useRouter();
const { threadId, setThreadId } = useThreadId();
const { reset } = useResetAtoms();
const { loading } = useStatus();
const { messageIds } = useMessageIds();
const [input, setInput] = useState("");
const basePath = `/app/${appSlug}`;
async function handleSendMessage() {
const userContent = input.trim();
setInput("");
try {
await sendMessage({ inputs: [{ type: "text", text: userContent }] });
} catch (error) {
console.error(error);
}
}
// Sync threadId from router query
useEffect(() => {
if (!router.query.threadId || Array.isArray(router.query.threadId)) return;
if (threadId === router.query.threadId) return;
setThreadId(router.query.threadId);
}, [router.query.threadId]);
return (
<ChatLayout
type="thread"
onNavigateBack={() => {
reset();
navigate(basePath);
}}
onThreadClick={(threadId) => {
reset();
navigate(`${basePath}/${threadId}`);
}}
>
<div data-chat-container data-is-empty={false}>
<MessagesArea
messageIds={messageIds}
error={null}
isLoading={loading}
/>
<MessageComposer
input={input}
setInput={setInput}
isLoading={loading}
isEmpty={false}
onSend={handleSendMessage}
onStop={stop}
>
<MessageComposerInput>
<MessageComposerTextarea />
<MessageComposerControls>
<MessageComposerSendButton />
</MessageComposerControls>
</MessageComposerInput>
<MessageComposerFeedback />
</MessageComposer>
</div>
</ChatLayout>
);
}The useAgentPlatform hook automatically detects the agent based on the URL path, and the threadId parameter is used to load the specific conversation. The useThreadId hook manages the thread ID state and syncs with the router query parameter.
Step 4: Create Agent Configurations
Agents are configured in your consuming application (not in the library). Create agent configuration files in your agents/ folder. For example, create agents/my-agent.ts:
import { createAgent } from "@vtex/agentic-ui";
export default createAgent({
slug: "my-agent",
id: "MY_AGENT_ID",
apiUrl: process.env.NEXT_PUBLIC_MY_AGENT_BASE_URL ?? "",
routes: {
frontend: "/app/my-agent",
stream: "/api/agent/responses",
tasks: "/api/agent/tasks",
},
settings: {
enableFeedback: true,
enableImageProcessing: false,
},
ui: {
name: "My Agent",
title: "My Agent Assistant",
subtitle: "Help with custom operations",
promptSuggestions: [
{
title: "Get Started",
description: "Learn what I can do",
promptText: "What operations can you help me with?",
},
{
title: "Analyze Data",
description: "Process and analyze your data",
promptText: "Help me analyze my sales data",
},
],
},
});Then create agents/index.ts to export all agents as an array:
import myAgent from "./my-agent";
export const agents = [myAgent];Import and use this array in your _app.tsx (as shown in Step 1). The useAgentPlatform hook will match the current route to the appropriate agent configuration. For more details, see Admin Integration documentation.
Step 5: Start Development
pnpm devYour agent interface is now ready! Users can:
- Start new conversations from the home page
- Continue existing conversations via thread pages
- Interact with tools and view rich content in the canvas
- Navigate between different threads seamlessly
Useful Links
Development
Local Development
When developing the agentic-ui package, changes will automatically reflect in dependent applications like @ui/. Start the development mode with:
pnpm install
pnpm devThis enables hot-reload, allowing you to see your changes immediately in any application that depends on this package.
Building
To build the package for production:
pnpm install
pnpm buildThis compiles the TypeScript code and generates the distribution files in the dist/ directory.
Publishing
To publish a new version to npm, follow these steps (be sure to be in the root of the repository):
Bump the version (choose
patch,minor, ormajor):pnpm lerna version patch --force-publish # or pnpm lerna version minor --force-publishPublish to npm:
pnpm lerna publish from-git
The version bump will create a git tag and update the package.json. Publishing from git ensures only tagged releases are published.
