Embeddable Chat Component
Bitte AI Chat
Introduction
The Bitte AI Chat component is a React component that enables AI-powered chat interactions in your application. It supports both NEAR Protocol and EVM blockchain interactions through wallet integrations, allowing users to interact with smart contracts and perform transactions directly through the chat interface.
🔑 Before you begin, make sure you have:
A Bitte API Key - Get your beta
BITTE_API_KEY
here
Quick Start (<10min)
Install package
Add the Chat Component
Setup API Route
Wallet Connection
1. Install Package
pnpm install @bitte-ai/chat
2. Add the Chat Component
Import and use BitteAiChat
in your react app and select the agent that you would like to use, browse the available agents and their respective ids on the registry
The apiUrl
corresponds to a proxy to not expose your api key on the client
import {BitteAiChat} from "@bitte-ai/chat";
import '@bitte-ai/chat/styles.css';
<BitteAiChat
agentId="your-agent-id"
apiUrl="/api/chat"
/>
3. Setup API Route
Create an API route in your Next.js application to proxy requests to the Bitte API to not expose your key. Nextjs app router implementation:
import type { NextRequest } from 'next/server';
const {
BITTE_API_KEY,
BITTE_API_URL = 'https://ai-runtime-446257178793.europe-west1.run.app/chat',
} = process.env;
export const dynamic = 'force-dynamic';
export const maxDuration = 60;
export const POST = async (req: NextRequest): Promise<Response> => {
try {
const data = await req.json();
const requestInit: RequestInit & { duplex: 'half' } = {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${BITTE_API_KEY}`,
},
duplex: 'half',
};
const upstreamResponse = await fetch(`${BITTE_API_URL}`, requestInit);
const headers = new Headers(upstreamResponse.headers);
headers.delete('Content-Encoding');
return new Response(upstreamResponse.body, {
status: upstreamResponse.status,
headers,
});
} catch (error) {
console.error('Error in chat API route:', error);
return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
};
Nextjs page router implementation:
import type { NextApiRequest, NextApiResponse } from 'next';
const {
BITTE_API_KEY,
BITTE_API_URL = 'https://ai-runtime-446257178793.europe-west1.run.app/chat',
} = process.env;
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const data = req.body;
const requestInit: RequestInit = {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${BITTE_API_KEY}`,
},
};
const upstreamResponse = await fetch(`${BITTE_API_URL}`, requestInit);
res.statusCode = upstreamResponse.status;
for (const [key, value] of Object.entries(upstreamResponse.headers)) {
if (key.toLowerCase() !== 'content-encoding') {
res.setHeader(key, value as string);
}
}
res.setHeader('Content-Type', upstreamResponse.headers.get('Content-Type') || 'application/json');
if (!upstreamResponse.body) {
return res.end();
}
const reader = upstreamResponse.body.getReader();
async function readChunk() {
const { done, value } = await reader.read();
if (done) {
return res.end();
}
res.write(value);
await readChunk();
}
await readChunk();
} catch (error) {
console.error('Error in chat API route:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
}
At this point the chat should already work but to be able to send transactions you will need to add a wallet connection
4. Add wallet connection
NEAR Integration
You can integrate with NEAR using either the NEAR Wallet Selector or a direct account connection. If you want to be able to send near transacitons through the chat you will need to define at least one of these
Using Wallet Selector
import { useBitteWallet, Wallet } from "@bitte-ai/react";
import { BitteAiChat } from "@bitte-ai/chat";
import '@bitte-ai/chat/styles.css';
export default function Chat() {
const { selector } = useBitteWallet();
const [wallet, setWallet] = useState<Wallet>();
useEffect(() => {
const fetchWallet = async () => {
const walletInstance = await selector.wallet();
setWallet(walletInstance);
};
if (selector) fetchWallet();
}, [selector]);
return (
<BitteAiChat
agentId="your-agent-id"
apiUrl="/api/chat"
wallet={{ near: { wallet } }}
/>
);
}
Using Direct Account
EVM Integration
EVM integration uses WalletConnect with wagmi hooks:
import { useAppKitAccount } from '@reown/appkit/react';
import { useSendTransaction, useSwitchChain } from 'wagmi';
export default function Chat() {
const { address } = useAppKitAccount();
const { data: hash, sendTransaction } = useSendTransaction();
const { switchChain } = useSwitchChain();
return (
<BitteAiChat
agentId="your-agent-id"
apiUrl="/api/chat"
wallet={{
evm: { address, hash, sendTransaction, switchChain },
}}
/>
);
}
import { Account } from "near-api-js";
// get near account instance from near-api-js by instantiating a keypair
<BitteAiChat
agentId="your-agent-id"
apiUrl="/api/chat"
wallet={{ near: { account: nearAccount } }}
/>
SUI Integration
SUI integration uses WalletConnect with wagmi hooks:
import { useWallet as useSuiWallet } from '@suiet/wallet-kit';
export default function Chat() {
const suiWallet = useSuiWallet();
return (
<BitteAiChat
agentId="your-agent-id"
apiUrl="/api/chat"
wallet={ sui: { wallet: suiWallet },}
/>
);
}
Component Props
interface BitteAiChatProps {
agentId: string; // ID of the AI agent to use
apiUrl: string; // Your API route path (e.g., "/api/chat")
historyApiUrl?: string; // Your history API route to keep context when signing transactions
wallet?: WalletOptions; // Wallet configuration
colors?: ChatComponentColors;
options?: {
agentName?: string; // Custom agent name
agentImage?: string; // Custom agent image URL
chatId?: string; // Custom chat ID
prompt?: string // Custom Initial prompt
localAgent?: {
pluginId: string;
accountId: string;
spec: BitteOpenAPISpec;
};
placeholderText?: string;
colors?: ChatComponentColors;
customComponents?: {
welcomeMessageComponent?: React.JSX.Element;
mobileInputExtraButton?: React.JSX.Element;
messageContainer?: React.ComponentType<MessageGroupComponentProps>;
chatContainer?: React.ComponentType<ChatContainerComponentProps>;
inputContainer?: React.ComponentType<InputContainerProps>;
sendButtonComponent?: React.ComponentType<SendButtonComponentProps>;
loadingIndicator?: React.ComponentType<LoadingIndicatorComponentProps>;
};
};
customToolComponents?: CustomToolComponent[];
}
export interface ReactToolComponent {
name: string;
component: React.ComponentType<CustomToolComponentProps>;
}
// Update CustomToolComponent type to align with BitteTool from primitives.ts
export type CustomToolComponent =
| PortfolioTool
| TransferTool
| SwapTool
| ReactToolComponent;
Custom Components
The BitteAiChat
component provides several UI elements that can be customized via the customComponents
under options prop. This enables full control over the appearance and behavior of key parts of the chat interface by passing in your own React components.
Available Custom Components
You can override the following built-in UI components:
customComponents?: {
welcomeMessageComponent?: React.JSX.Element;
mobileInputExtraButton?: React.JSX.Element;
messageContainer?: React.ComponentType<MessageGroupComponentProps>;
chatContainer?: React.ComponentType<ChatContainerComponentProps>;
inputContainer?: React.ComponentType<InputContainerProps>;
sendButtonComponent?: React.ComponentType<SendButtonComponentProps>;
loadingIndicator?: React.ComponentType<LoadingIndicatorComponentProps>;
};
Component Types
Below are the expected prop types for each customizable component:
MessageGroupComponentProps
export interface MessageGroupComponentProps {
message: SmartActionAiMessage;
isUser: boolean;
userName: string;
children: React.ReactNode;
style: {
backgroundColor: string;
borderColor: string;
textColor: string;
};
uniqueKey: string;
}
ChatContainerComponentProps
export interface ChatContainerComponentProps {
children: React.ReactNode;
style?: {
backgroundColor?: string;
borderColor?: string;
};
}
InputContainerProps
export interface InputContainerProps {
children: React.ReactNode;
style?: {
backgroundColor?: string;
borderColor?: string;
};
}
SendButtonComponentProps
export interface SendButtonComponentProps {
input: string;
isLoading: boolean;
buttonColor?: string;
textColor?: string;
onClick?: () => void;
}
LoadingIndicatorComponentProps
export interface LoadingIndicatorComponentProps {
textColor?: string;
}
Example Usage
Here's how you might configure the BitteAiChat
component, including some optional props and custom components:
<BitteAiChat
agentId={searchParams.get('agentId') || bitteAgent?.id || 'bitte-assistant'}
apiUrl="/api/chat"
historyApiUrl="/api/history"
options={{
agentName: bitteAgent?.name,
agentImage: bitteAgent?.image,
colors: chatColors,
}}
wallet={{
near: { wallet },
evm: evmAdapter,
}}
customComponents={{
welcomeMessageComponent: <CustomWelcome />,
mobileInputExtraButton: <ExtraMobileButton />,
messageContainer: CustomMessageGroup,
chatContainer: CustomChatContainer,
inputContainer: CustomInputContainer,
sendButtonComponent: CustomSendButton,
loadingIndicator: CustomLoadingIndicator,
}}
/>
✅ Tip: All custom components are optional. If not provided, the default components will be used.
Custom Tool Components
The BitteAiChat
component supports embedding interactive, data-driven tools directly into the chat UI through the customToolComponents
prop. These tools allow users to interact with structured data returned by your agent’s API, such as portfolios, token swaps, or custom workflows.
How Tool Rendering Works
When your AI agent responds with a message that includes a data
field , and that data
matches a known type (like PortfolioResponse
, TransferFTData
, etc.), the chat interface can automatically render the corresponding tool component — as long as a tool is registered in customToolComponents
with a matching name
.
The name
must match:
The operation name in your agent’s OpenAPI spec
The
name
field in the tool component configuration
There is no need to wrap the response in a tool
object — the chat package dynamically maps the data
to the corresponding component based on the configured name and type.
Pre-Configured Tools
The @bitte-ai/chat
package comes with built-in support for the following tools:
get-portfolio
swap
transfer-ft
As long as:
The agent's API response includes a
data
field that matches the correct type (PortfolioResponse
,SwapFTData
, orTransferFTData
— all from@bitte-ai/types
)The OpenAPI spec defines the route using one of the above names (i.e.
get-portfolio
,swap
, ortransfer-ft
as operation IDs or paths)
...then the matching tool UI will automatically be rendered — no manual configuration required.
These tools are auto-wired and will display a default UI out-of-the-box. You only need to use customToolComponents
if you want to override these default UIs or add custom tools.
Example: Registering Tool Components
const customToolComponents = [
{
name: 'get-portfolio', // This should match the operationId or route name from your OpenAPI spec
component: (props) => (
<TokenList data={props.data as PortfolioResponse} />
),
},
{
name: 'swap',
component: (props) => (
<Swap data={props.data as SwapFTData} />
),
},
{
name: 'transfer-ft',
component: (props) => (
<Transfer data={props.data as TransferFTData} />
),
},
{
name: 'my-custom-tool',
component: (props) => <MyCustomComponent data={props.data} />,
},
];
Then pass them into BitteAiChat
:
<BitteAiChat
agentId={searchParams.get('agentId') || bitteAgent?.id || 'bitte-assistant'}
apiUrl="/api/chat"
historyApiUrl="/api/history"
wallet={{
near: { wallet },
evm: evmAdapter,
}}
options={{
agentName: bitteAgent?.name,
agentImage: bitteAgent?.image,
colors: chatColors,
}}
customToolComponents={customToolComponents}
/>
Type Safety & Tool Interfaces
All tool types and data interfaces are available from @bitte-ai/types
Available Agents
Agent Name
Description
Category
Agent ID
CoWSwap Assistant
An assistant that generates transaction data for CoW Protocol Interactions
DeFi
near-cow-agent.vercel.app
Meme.cooking
Generates a transaction payload for creating a new memecoin on the meme.cooking platform
Memes
meme-cooking-bitte-agent.vercel.app
CoinGecko Agent
Provides real-time cryptocurrency data, including prices, market information, and charts
Investing
coingecko-ai.vercel.app
Ref Finance Agent
Provides token metadata and swaps tokens through Ref Finance
DeFi
ref-finance-agent.vercel.app
NEAR Roast Agent
Roasts a NEAR account based on their on-chain activity
Other
near-roast-agent.vercel.app
NEAR Staking
Stake, unstake, and see staking information
Other
staking-agent.intear.tech
Crypto Technical Analyst
Provides in-depth market analysis and sophisticated trading insights based on technical analysis
Investing
technical-analysis-agent.vercel.app
DAO Agent
Interacts with Sputnik DAO Contracts, query DAOs, create proposals, and vote on proposals
DAO
dao-agent.vercel.app
Safe Account Assistant
Manages Near{Safe} Account Structure
Wallet Management
near-safe-agent.vercel.app
Uniswap Assistant
Generates transaction data for Uniswap V3 Interactions
Other
near-uniswap-agent.vercel.app
GrowthMate Discovery
Discovers the ecosystem through relevant activities, news, and offers
Ecosystem
bitte-agent.vercel.app
Python Code Runner
A helpful assistant for running Python code snippets
Computational
bitte-wasmer-agent.fly.dev
PotLock Assistant
Helps users search projects and donations on PotLock platform and create projects
Other
potlockaiagent-hqd5dzcjajhpc3fa.eastus-01.azurewebsites.net
Delta Trade DCA Helper
Helps set up DCA plans to buy NEAR and other tokens
Investing
dcaagent.deltatrade.ai
SUI Defi Agent
check balances, swap, stake and more on SUI
Defi
bitte-sui-agent.vercel.app
Sui Explorer
Retrieve any blockchain data from SUI
Ecosystem
sui-explorer.vercel.app
Benqi Agent
Defi Agent for Benqi Protocol on Avalanche
Defi
benqi-agent.vercel.app
Bitte WETH Wraptor
Wrap and unwrap ETH.
Other
wraptor-agent.vercel.app
Bitte Distribute Tokens
Distribute tokens on EVM chains using csv files and more
Other
safe-airdrop-agent.vercel.app
Creating Custom Agents
Check out our docs for creating your own agent from an API
Example Projects
Styling
The component can be customized using the colors
prop:
type ChatComponentColors = {
generalBackground?: string; // Chat container background
messageBackground?: string; // Message bubble background
textColor?: string; // Text color
buttonColor?: string; // Button color
borderColor?: string; // Border color
};
Browser Redirects (Optional)
if you're having issues with your app or wallet containing redirects you can optionally use a history api to maintain context
Create an API route in your Next.js application that will allow your app if you want to save chat context when signing a transaction after getting redirected to the wallet.
import { type NextRequest, NextResponse } from 'next/server';
const { BITTE_API_KEY, BITTE_API_URL = 'https://wallet.bitte.ai/api/v1' } =
process.env;
export const dynamic = 'force-dynamic';
export const maxDuration = 60;
export const GET = async (req: NextRequest): Promise<NextResponse> => {
const { searchParams } = new URL(req.url);
const id = searchParams.get('id');
const url = `${BITTE_API_URL}/history?id=${id}`;
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${BITTE_API_KEY}`,
},
});
const result = await response.json();
return NextResponse.json(result);
};
Last updated