npm install @stream-io/chat-ai-sdk
# or
yarn add @stream-io/chat-ai-sdkStream Chat AI SDK
The Stream Chat AI SDK has extensive tooling for hosting AI assistants inside Stream Chat channels. The SDK wires Stream’s real-time messaging primitives to the Vercel AI SDK so that you can connect OpenAI, Anthropic, Gemini, or xAI models, stream responses with typing indicators, dispatch tool calls, and optionally persist long-term memory via Mem0. This page walks you through installation, configuration, architecture, and the exposed APIs.
You can find sample integration with the Stream Chat AI SDK in our samples repo.
Installation
The package ships TypeScript sources plus transpiled JavaScript under dist/. You can import either ESM or CJS builds depending on your bundler.
Supported Providers
Select a provider at runtime through the AgentPlatform enum:
| Platform | Enum | Default model | Notes |
|---|---|---|---|
| OpenAI | AgentPlatform.OPENAI | gpt-4o-mini | Enables Mem0 (provider: openai) when configured. |
| Anthropic | AgentPlatform.ANTHROPIC | claude-3-5-sonnet-20241022 | Uses the Anthropic Vercel connector with optional Mem0. |
| Gemini | AgentPlatform.GEMINI | gemini-1.5-flash | Accepts GOOGLE_GENERATIVE_AI_API_KEY or GEMINI_API_KEY. |
| xAI | AgentPlatform.XAI | grok-beta | No Mem0 integration yet because Mem0 does not expose xAI. |
Override the model ID per agent via the model option.
Environment Variables
Set the following before instantiating an agent:
| Variable | Required | Description |
|---|---|---|
STREAM_API_KEY, STREAM_API_SECRET | ✅ | Used by the server-side Stream client in serverClient.ts to upsert and connect the agent user. Missing keys throw immediately on import. |
OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY/GEMINI_API_KEY, XAI_API_KEY | ✅ (per provider) | API key supplied to the Vercel AI SDK wrapper for the configured AgentPlatform. |
MEM0_API_KEY, MEM0_CONFIG_JSON, MEM0_DEFAULT_* | Optional | Enables Mem0 memory when present. See Memory & Mem0. |
OPENWEATHER_API_KEY | Optional | Required only if you rely on the getCurrentTemperature tool returned by createDefaultTools(). |
Tip: when running multiple agents, load environment variables once at process start-up so lazy imports such as
serverClientdo not throw intermittently.
Quick Start: Single Agent
import {
Agent,
AgentPlatform,
createDefaultTools,
type ClientToolDefinition,
} from "@stream-io/chat-ai-sdk";
const agent = new Agent({
userId: "ai-bot-weather",
channelId: "support-room",
channelType: "messaging",
platform: AgentPlatform.OPENAI,
model: "gpt-4o-mini",
instructions: [
"Answer in a friendly, concise tone.",
"Prefer Celsius unless the user specifies otherwise.",
],
serverTools: createDefaultTools(),
clientTools: [
{
name: "openHelpCenter",
description: "Open the help center in the web app",
parameters: {
type: "object",
properties: { articleSlug: { type: "string" } },
required: ["articleSlug"],
},
} satisfies ClientToolDefinition,
],
mem0Context: {
channelId: "support-room",
appId: "stream-chat-support",
},
});
await agent.start();
// Later...
await agent.stop();Multi-Channel Deployments with AgentManager
Use AgentManager to orchestrate many concurrent agents without leaking resources. It caches instances by userId, supports lazy start/stop, and periodically disposes idle bots.
import {
AgentManager,
AgentPlatform,
createDefaultTools,
} from "@stream-io/chat-ai-sdk";
const manager = new AgentManager({
serverToolsFactory: () => createDefaultTools(),
inactivityThresholdMs: 15 * 60 * 1000,
cleanupIntervalMs: 5_000,
agentIdResolver: (channelId) => `ai-${channelId}`,
});
await manager.startAgent({
userId: "ai-support-bot-123",
channelId: "support-room",
channelType: "messaging",
platform: AgentPlatform.OPENAI,
instructions: "Answer with step-by-step troubleshooting tips.",
});
manager.registerClientTools("support-room", [
{
name: "openTicket",
description: "Open the CRM ticket in the dashboard",
parameters: {
type: "object",
properties: { ticketId: { type: "string" } },
required: ["ticketId"],
},
},
]);
// On shutdown:
await manager.stopAgent("ai-support-bot-123");
manager.dispose();Key behaviors:
serverToolsFactoryruns each time a new agent is constructed, allowing per-channel config.registerClientTools(channelId, tools)persists definitions and re-applies them the next time the channel’s agent starts.inactivityThresholdMs(default ~8 hours) pairs withcleanupIntervalMs(default 5 seconds) to stop agents automatically onceAgent#getLastInteraction()stales.dispose()clears the cleanup interval and caches; call it during process shutdown to avoid hanging timers.
Working with Tools
Server Tools (AgentTool)
Server tools run in Node/TS and receive typed arguments plus channel/message context. Each tool consists of a name, human-facing description, optional instructions (folded into the system prompt), a Zod parameters schema, and an async execute function that returns the tool output text.
import { z } from "zod";
import type { AgentTool } from "@stream-io/chat-ai-sdk";
const getOrderStatus: AgentTool = {
name: "getOrderStatus",
description: "Look up order details by ID.",
instructions: "Invoke only when the user provides a valid order number.",
parameters: z.object({
orderId: z.string().describe("The e-commerce order number"),
}),
async execute({ orderId }, { channel }) {
const record = await lookupOrder(orderId);
if (!record) {
throw new Error(`No order found for ${orderId}`);
}
await channel.sendEvent({ type: "order.viewed", orderId });
return `Status: ${record.status}. ETA ${record.eta}`;
},
};Register tools through serverTools in the Agent constructor, agent.addServerTools(...), or agent.registerServerTools([...], { replace: boolean }).
Client Tools
Client tools are JSON-schema definitions representing capabilities that must be executed on the front end (e.g., “open ticket in CRM”, “navigate to screen”). The SDK converts the schema to a Zod validator and exposes an internal tool that sends a custom_client_tool_invocation event containing:
{
"type": "custom_client_tool_invocation",
"cid": "<channel-id>",
"message_id": "<message-id>",
"tool": { "name": "openTicket", ... },
"args": { "ticketId": "123" }
}Listen for that event in your app, perform the privileged action, and optionally respond in chat.
Default Tools
createDefaultTools() returns getCurrentTemperature. It requires OPENWEATHER_API_KEY and demonstrates:
- Fetching an external REST API (
axios). - Converting units based on tool arguments.
- Supplying
instructionsso the LLM only calls the tool when the user explicitly asks about weather.
You can use this tool as an inspiration for production tools.
Streaming, Indicators, and Cancellation
- Messages stream via
streamText/textStreamorfullStreamdepending on provider features. ai_indicator.updateevents surface typing states:AI_STATE_THINKING,AI_STATE_GENERATING,AI_STATE_EXTERNAL_SOURCES, andAI_STATE_ERROR.ai_indicator.clearflushes once the final response lands.- The SDK listens for
ai_indicator.stopevents and aborts the underlying AbortController, which cancelsstreamTextmid-flight. - Partial message text is written through
chatClient.partialUpdateMessage, enabling front-end live typing plus cancellation UI.
Memory & Mem0
Mem0 adds “long-term” recall for user+channel pairs. Configuration layers:
- Optional
MEM0_CONFIG_JSON(stringified JSON) provides defaults such asmetadata. - Per-process env fallbacks:
MEM0_DEFAULT_USER_ID,MEM0_DEFAULT_AGENT_ID,MEM0_DEFAULT_APP_ID. Agentoptionmem0Contextlets you overridechannelId,userId,appId,agentId, or supplyconfigOverrides.- During runtime the SDK augments metadata with Stream-specific IDs.
Mem0 only runs when the chosen provider matches openai, anthropic, or google and MEM0_API_KEY is set. Pass { disableMem0: true } via Agent.generateSummary(...) implicitly when generating summaries to keep them fast and deterministic.
Summaries and Titles
Use agent.summarize(text) (instance) or Agent.generateSummary(text, platform, model?) (static) to request a short, six-word “title” summarizing arbitrary text—ideal for channel list previews, inbox cards, or push notification headers. The helper:
- Spins up a lightweight language model using the current provider settings.
- Disables Mem0 to avoid unnecessary persistence.
- Normalizes the resulting string by trimming surrounding quotes.
const headline = await agent.summarize(latestTranscript);
await channel.updatePartial({ set: { subtitle: headline } });