The @packages/agent package provides a powerful SDK for building AI agents with Convex, built on top of Vercel’s AI SDK.
Installation
npm install @packages/agent
Overview
This package provides:
- Agent class for managing AI conversations
- Thread management for conversation history
- Message storage in Convex
- Tool integration for function calling
- Streaming support for real-time responses
- Context management with RAG (Retrieval Augmented Generation)
- React hooks for UI integration
Quick Start
import { Agent } from "@packages/agent";
import { openai } from "@ai-sdk/openai";
import { components } from "./convex/_generated/api";
const agent = new Agent(components.agent, {
name: "my-agent",
languageModel: openai.chat("gpt-4o"),
instructions: "You are a helpful assistant.",
});
Core Concepts
Agent Class
The Agent class is the main entry point for building AI agents:
The Convex agent component for data persistence
Configuration options for the agentName of the agent (attributed to messages)
AI SDK language model (e.g., openai.chat("gpt-4o"))
System prompt for the agent
Conditions for stopping generation
Creating a Thread
Threads represent conversation sessions:
import { action } from "./_generated/server";
export const startConversation = action(async (ctx) => {
const { threadId, thread } = await agent.createThread(ctx, {
userId: "user_123",
title: "My Conversation",
});
// Use the thread for generation
const result = await thread.generateText({
prompt: "Hello, how are you?",
});
return { threadId, response: result.text };
});
Create a new conversation threadUser ID to associate with the thread
Returns: { threadId: string, thread: Thread }
Continuing a Thread
export const continueConversation = action(async (ctx, { threadId }) => {
const { thread } = await agent.continueThread(ctx, { threadId });
const result = await thread.generateText({
prompt: "Tell me more about that.",
});
return result.text;
});
Text Generation
Generate Text
Generate text synchronously (waits for complete response):
const result = await agent.generateText(ctx,
{ threadId, userId },
{
prompt: "What is the capital of France?",
maxSteps: 3,
}
);
console.log(result.text); // "Paris is the capital of France."
console.log(result.usage); // Token usage stats
Generate text synchronouslyprompt
string | ModelMessage[]
required
The user prompt or messages
Maximum number of tool call steps
Override system instructions
Returns: GenerateTextResult with text, usage, finishReason
Stream Text
Stream text for real-time responses:
const stream = await agent.streamText(ctx,
{ threadId, userId },
{
prompt: "Write a story about a robot.",
},
{
saveStreamDeltas: true, // Save streaming chunks to DB
}
);
// Stream to client
for await (const chunk of stream.textStream) {
console.log(chunk);
}
const fullText = await stream.text;
Generate and stream text in real-timesaveStreamDeltas
boolean | StreamingOptions
Whether to save streaming chunks to the database
Returns: StreamTextResult with textStream, text, finishReason
Object Generation
Generate structured objects using schemas:
import { z } from "zod";
const result = await agent.generateObject(ctx,
{ threadId },
{
schema: z.object({
name: z.string(),
age: z.number(),
email: z.string().email(),
}),
prompt: "Extract: John is 30 years old, email john@example.com",
}
);
console.log(result.object);
// { name: "John", age: 30, email: "john@example.com" }
Generate structured objectsschema
ZodSchema | JSONSchema
required
Schema for the output object
Returns: GenerateObjectResult with typed object
Create tools for agents to call:
import { createTool } from "@packages/agent";
import { z } from "zod";
const weatherTool = createTool({
args: z.object({
city: z.string(),
}),
description: "Get the weather for a city",
handler: async (ctx, { city }) => {
// Fetch weather data
return `The weather in ${city} is sunny.`;
},
});
const agent = new Agent(components.agent, {
name: "weather-agent",
languageModel: openai.chat("gpt-4o"),
tools: { weather: weatherTool },
});
Create a tool for agent function callingZod schema for tool arguments
Description of what the tool does
Handler function: (ctx: ToolCtx, args) => Promise<string | object>
Message Management
Save Messages
const { messageId } = await agent.saveMessage(ctx, {
threadId,
userId,
message: {
role: "user",
content: "Hello!",
},
});
List Messages
const messages = await agent.listMessages(ctx, {
threadId,
paginationOpts: { numItems: 50 },
excludeToolMessages: true,
});
Delete Messages
// Delete specific messages
await agent.deleteMessages(ctx, {
messageIds: ["msg_123", "msg_456"],
});
// Delete by range
await agent.deleteMessageRange(ctx, {
threadId,
startOrder: 1,
endOrder: 5,
});
Context & RAG
Fetch relevant context from previous messages:
const contextMessages = await agent.fetchContextMessages(ctx, {
userId,
threadId,
searchText: "How do I deploy?",
contextOptions: {
maxMessages: 10,
vectorSearch: true,
},
});
React Integration
Use with React hooks:
import { useThreadMessages } from "@packages/agent/react";
import { useQuery } from "convex/react";
function ChatInterface({ threadId }) {
const messages = useThreadMessages(threadId);
return (
<div>
{messages.map(msg => (
<div key={msg._id}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
</div>
);
}
Exports
Main agent exports - Agent, createTool, message utilities
Validation schemas for messages and threads
React hooks for agent integration
Testing
import { mockModel } from "@packages/agent";
const testAgent = new Agent(components.agent, {
name: "test-agent",
languageModel: mockModel({
responses: ["Hello!", "How can I help?"],
}),
});