Streaming Chat Agent
Real-time commerce chat with token-by-token streaming. Next.js App Router + Vercel AI SDK. Tool calls appear in the UI as they happen.
Real-time commerce chat with token-by-token streaming. Next.js App Router + Vercel AI SDK. Tool calls appear in the UI as they happen — the canonical customer-facing chat pattern.
Prerequisites
npm install @codespar/sdk @codespar/vercel ai @ai-sdk/openaiBackend route
The API route creates a session per request, gets tools, and returns a streaming response. The Vercel adapter handles tool execution automatically — no manual loop.
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/vercel";
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY! });
export async function POST(req: Request) {
const { messages } = await req.json();
const session = await codespar.sessions.create({
servers: ["stripe", "asaas", "correios"],
});
const tools = await getTools(session);
const result = streamText({
model: openai("gpt-4o"),
tools,
maxSteps: 10,
system: `Commerce assistant for a Brazilian store. Be concise.`,
messages,
onFinish: async () => { await session.close(); },
});
return result.toDataStreamResponse();
}The Vercel adapter's getTools returns tools with built-in execute methods. Tool calls happen automatically as the LLM requests them — no manual loop needed.
Frontend component
"use client";
import { useChat } from "@ai-sdk/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } =
useChat({ api: "/api/chat" });
return (
<div>
{messages.map((m) => (
<div key={m.id}>{m.content}</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
</form>
</div>
);
}How it works
- User types a message,
useChatsends it to/api/chat - Backend creates a CodeSpar session with the servers you want available
streamTextstarts streaming the LLM response token-by-token- When the LLM decides to call a tool, Vercel AI SDK executes it via
session.execute()automatically - Tool results feed back into the stream, the LLM continues generating
- Session closes when the response finishes — via
onFinish
Adding tool call indicators
Show the user when tools are being called — dramatically improves perceived latency and trust.
{messages.map((m) => (
<div key={m.id}>
{m.content && <p>{m.content}</p>}
{m.toolInvocations?.map((tool) => (
<div key={tool.toolCallId}>
{tool.state === "result"
? `✓ ${tool.toolName} completed`
: `⏳ Calling ${tool.toolName}...`}
</div>
))}
</div>
))}Production considerations
- Session per request. Each
/api/chatcall creates a new session. For multi-turn conversations where you want to reuse a session, store the session ID client-side and reopen it. - Error handling. Wrap
session.close()inonFinish— it runs on both success and error paths. - Rate limiting. Add rate limiting to the API route to prevent abuse.
- Authentication. Use Clerk or NextAuth to authenticate users before creating sessions. Pass
user_idtosessions.create()for billing attribution.
Next steps
Multi-Provider Agent
Route payments across Stripe, Asaas, and Mercado Pago automatically. The agent calls codespar_pay — CodeSpar picks the best provider for each transaction.
Webhook Listener
React to payment webhooks with a deterministic loop. On payment.confirmed, automatically issue an NF-e, create shipping, and send WhatsApp — no agent, no LLM, no surprise.