Install the required packages for the Vercel AI SDK and Inconvo integration.
npm install ai @ai-sdk/react @inconvoai/vercel-ai-sdkBuild AI-powered chat interfaces with data visualization using the Vercel AI SDK.
The @inconvoai/vercel-ai-sdk package integrates Inconvo’s data agent capabilities directly into the Vercel AI SDK, enabling AI models to query databases and generate interactive visualizations through natural language.
Install the required packages for the Vercel AI SDK and Inconvo integration.
npm install ai @ai-sdk/react @inconvoai/vercel-ai-sdk
Add your Inconvo API credentials and AI provider API key to your .env.local file.
INCONVO_API_KEY="$YOUR_API_KEY"INCONVO_AGENT_ID="$YOUR_AGENT_ID"AI_GATEWAY_API_KEY="$YOUR_AI_PROVIDER_API_KEY"
Create an API route that uses the Vercel AI SDK with the Inconvo data agent tool. This enables the AI model to query your data and generate visualizations.
import { streamText, convertToModelMessages, stepCountIs } from "ai";import { inconvoDataAgent } from "@inconvoai/vercel-ai-sdk";
export async function POST(req: Request) { const { messages } = await req.json();
const result = streamText({ model: "openai/gpt-4", // or your preferred model system: `When you receive structured data (tables, charts) from tools, do NOT recreate or reformat them as markdown tables in your response. The tool output will be displayed directly as interactive UI. You may provide brief context, insights, or follow-up suggestions, but never duplicate the data itself.`, messages: await convertToModelMessages(messages), tools: { ...inconvoDataAgent({ agentId: process.env.INCONVO_AGENT_ID!, userIdentifier: "user-123", userContext: { organisationId: 1, }, }), }, stopWhen: stepCountIs(5), });
return result.toUIMessageStreamResponse();}
Create React components to render the different response types from Inconvo (text, table, chart).
You’ll need components for:
Use the useChat hook from @ai-sdk/react to build an interactive chat interface with support for Inconvo tool results.
"use client";
import { useChat } from "@ai-sdk/react";import { useState } from "react";import { InconvoToolResult } from "./components/inconvo/InconvoToolResult";
function isInconvoOutput(output: unknown): boolean { return ( typeof output === "object" && output !== null && "type" in output && typeof (output as any).type === "string" && ["text", "table", "chart"].includes((output as any).type) );}
export default function Chat() { const [input, setInput] = useState(""); const { messages, sendMessage } = useChat();
return ( <div className="flex flex-col w-full max-w-4xl py-24 mx-auto px-4"> {messages.map((message) => ( <div key={message.id} className="mb-4"> <div className="font-semibold mb-1"> {message.role === "user" ? "User" : "AI"}: </div> <div> {message.parts.map((part, i) => { if (part.type === "text") { return ( <div key={`${message.id}-${i}`} className="whitespace-pre-wrap" > {part.text} </div> ); }
// Handle Inconvo tool calls if (part.type.startsWith("tool-") && "state" in part) { const isInconvoTool = part.type.includes("DataAgent");
// Show loading state while tool is executing if (part.state === "input-available" && isInconvoTool) { return ( <div key={`${message.id}-${i}`} className="flex items-center gap-2 p-4 text-sm text-zinc-500" > <div className="animate-spin h-4 w-4 border-2 border-zinc-300 border-t-zinc-600 rounded-full" /> <div>Querying your data...</div> </div> ); }
// Show result when available if ( part.state === "output-available" && "output" in part && isInconvoOutput(part.output) ) { return ( <InconvoToolResult key={`${message.id}-${i}`} result={part.output} /> ); } }
return null; })} </div> </div> ))}
<form onSubmit={(e) => { e.preventDefault(); sendMessage({ text: input }); setInput(""); }} > <input className="fixed bottom-0 w-full max-w-4xl p-2 mb-8 border border-zinc-300 rounded shadow-xl" value={input} placeholder="Ask about your data..." onChange={(e) => setInput(e.currentTarget.value)} /> </form> </div> );}