66
77AI Agent framework built on Convex.
88
9- - Automatic storage of chat history, per-user or per-chat .
10- - RAG for chat context, via hybrid text & vector search, with configuration options.
9+ - Automatic storage of thread history, per-user or per-thread .
10+ - RAG for thread context, via hybrid text & vector search, with configuration options.
1111 Or use the API to query the history yourself and do it your way.
12- - Opt-in search for messages from other chats (for the same specifieduser).
12+ - Opt-in search for messages from other threads (for the same specifieduser).
1313- Tool calls via the AI SDK, along with Convex-specific helpers.
1414- Easy workflow integration with the [ Workflow component] ( https://convex.dev/components/workflow ) .
15- - Reactive & realtime updates to asynchronous chats .
15+ - Reactive & realtime updates to asynchronous threads .
1616- Support for streaming text and storing the result in the database.
17- - Optionally filter tool calls out of the chat history.
17+ - Optionally filter tool calls out of the thread history.
1818
1919Example usage:
2020
2121``` ts
2222// Define an agent similarly to the AI SDK
2323const supportAgent = new Agent (components .agent , {
24- chat : openai .chat (" gpt-4o-mini" ),
24+ thread : openai .chat (" gpt-4o-mini" ),
2525 textEmbedding: openai .embedding (" text-embedding-3-small" ),
2626 instructions: " You are a helpful assistant." ,
2727 tools: { accountLookup , fileTicket , sendEmail },
2828});
2929
3030// Use the agent from within a normal action:
31- export const createChatting = action ({
31+ export const createThread = action ({
3232 args: { prompt: v .string (), userId: v .string () },
33- handler : async (ctx , { prompt , userId }): Promise <{ chatId : string ; initialResponse: string }> => {
34- // Start a new chat for the user.
35- const { chatId, chat } = await supportAgent .createChat (ctx , { userId });
36- const result = await chat .generateText ({ prompt });
37- return { chatId , initialResponse: result .text };
33+ handler : async (ctx , { prompt , userId }): Promise <{ threadId : string ; initialResponse: string }> => {
34+ // Start a new thread for the user.
35+ const { threadId, thread } = await supportAgent .createThread (ctx , { userId });
36+ const result = await thread .generateText ({ prompt });
37+ return { threadId , initialResponse: result .text };
3838 },
3939});
4040
4141// Pick up where you left off:
42- export const continueChat = action ({
43- args: { prompt: v .string (), chatId : v .string () },
44- handler : async (ctx , { prompt , chatId }): Promise <string > => {
45- // This includes previous message history from the chat automatically.
46- const { chat } = await supportAgent .continueChat (ctx , { chatId });
47- const result = await chat .generateText ({ prompt });
42+ export const continueThread = action ({
43+ args: { prompt: v .string (), threadId : v .string () },
44+ handler : async (ctx , { prompt , threadId }): Promise <string > => {
45+ // This includes previous message history from the thread automatically.
46+ const { thread } = await supportAgent .continueThread (ctx , { threadId });
47+ const result = await thread .generateText ({ prompt });
4848 return result .text ;
4949 },
5050});
@@ -56,10 +56,10 @@ const workflow = new WorkflowManager(components.workflow);
5656const s = internal .example ; // where steps are defined
5757
5858export const supportAgentWorkflow = workflow .define ({
59- args: { prompt: v .string (), userId: v .string (), chatId : v .string () },
60- handler : async (step , { prompt , userId , chatId }) => {
59+ args: { prompt: v .string (), userId: v .string (), threadId : v .string () },
60+ handler : async (step , { prompt , userId , threadId }) => {
6161 const suggestion = await step .runAction (s .supportAgentStep , {
62- chatId , generateText: { prompt },
62+ threadId , generateText: { prompt },
6363 });
6464 const polished = await step .runAction (s .adaptSuggestionForUser , {
6565 suggestion , userId ,
@@ -118,7 +118,7 @@ import { Agent } from "@convex-dev/agent";
118118// Define an agent similarly to the AI SDK
119119const supportAgent = new Agent (components .agent , {
120120 // Note: all of these are optional.
121- chat : openai .chat (" gpt-4o-mini" ),
121+ thread : openai .chat (" gpt-4o-mini" ),
122122 // Used for vector search (RAG).
123123 textEmbedding: openai .embedding (" text-embedding-3-small" ),
124124 // Will be the default system prompt if not overriden.
@@ -142,9 +142,9 @@ const supportAgent = new Agent(components.agent, {
142142 // How many recent messages to include. These are added after the search
143143 // messages, and do not count against the search limit.
144144 recentMessages: 10 ,
145- // Whether to search across other chats for relevant messages.
146- // By default, only the current chat is searched.
147- searchOtherChats : true ,
145+ // Whether to search across other threads for relevant messages.
146+ // By default, only the current thread is searched.
147+ searchOtherThreads : true ,
148148 // Options for searching messages.
149149 searchOptions: {
150150 // The maximum number of messages to fetch.
@@ -163,7 +163,7 @@ const supportAgent = new Agent(components.agent, {
163163 storageOptions: {
164164 // Defaults to false, allowing you to pass in arbitrary context that will
165165 // be in addition to automatically fetched content.
166- // Pass true to have all input messages saved to the chat history.
166+ // Pass true to have all input messages saved to the thread history.
167167 saveAllInputMessages: true ,
168168 // Defaults to true
169169 saveOutputMessages: true ,
@@ -175,35 +175,35 @@ const supportAgent = new Agent(components.agent, {
175175});
176176```
177177
178- ### Starting a chat
178+ ### Starting a thread
179179
180- You can start a chat from either an action or a mutation.
180+ You can start a thread from either an action or a mutation.
181181If it's in an action, you can also start sending messages.
182- The chatId allows you to resume later and maintain message history.
182+ The threadId allows you to resume later and maintain message history.
183183
184184``` ts
185185// Use the agent from within a normal action:
186- export const createChatting = action ({
186+ export const createThread = action ({
187187 args: { prompt: v .string (), userId: v .string () },
188- handler : async (ctx , { prompt , userId }): Promise <{ chatId : string ; initialResponse: string }> => {
189- // Start a new chat for the user.
190- const { chatId, chat } = await supportAgent .createChat (ctx , { userId });
191- const result = await chat .generateText ({ prompt });
192- return { chatId , initialResponse: result .text };
188+ handler : async (ctx , { prompt , userId }): Promise <{ threadId : string ; initialResponse: string }> => {
189+ // Start a new thread for the user.
190+ const { threadId, thread } = await supportAgent .createThread (ctx , { userId });
191+ const result = await thread .generateText ({ prompt });
192+ return { threadId , initialResponse: result .text };
193193 },
194194});
195195```
196196
197- ### Continuing a chat
197+ ### Continuing a thread
198198
199199``` ts
200200// Pick up where you left off:
201- export const continueChat = action ({
202- args: { prompt: v .string (), chatId : v .string () },
203- handler : async (ctx , { prompt , chatId }): Promise <string > => {
204- // This includes previous message history from the chat automatically.
205- const { chat } = await supportAgent .continueChat (ctx , { chatId });
206- const result = await chat .generateText ({ prompt });
201+ export const continueThread = action ({
202+ args: { prompt: v .string (), threadId : v .string () },
203+ handler : async (ctx , { prompt , threadId }): Promise <string > => {
204+ // This includes previous message history from the thread automatically.
205+ const { thread } = await supportAgent .continueThread (ctx , { threadId });
206+ const result = await thread .generateText ({ prompt });
207207 return result .text ;
208208 },
209209});
@@ -216,10 +216,10 @@ export const supportAgentStep = supportAgent.asAction({ maxSteps: 10 });
216216
217217// Then from within another action:
218218export const callSupportAgent = action ({
219- args: { prompt: v .string (), userId: v .string (), chatId : v .string () },
220- handler : async (step , { prompt , userId , chatId }) => {
219+ args: { prompt: v .string (), userId: v .string (), threadId : v .string () },
220+ handler : async (step , { prompt , userId , threadId }) => {
221221 const suggestion = await step .runAction (s .supportAgentStep , {
222- chatId , userId , generateText: { prompt },
222+ threadId , userId , generateText: { prompt },
223223 });
224224 },
225225});
@@ -237,10 +237,10 @@ const workflow = new WorkflowManager(components.workflow);
237237const s = internal .example ; // where steps are defined
238238
239239export const supportAgentWorkflow = workflow .define ({
240- args: { prompt: v .string (), userId: v .string (), chatId : v .string () },
241- handler : async (step , { prompt , userId , chatId }) => {
240+ args: { prompt: v .string (), userId: v .string (), threadId : v .string () },
241+ handler : async (step , { prompt , userId , threadId }) => {
242242 const suggestion = await step .runAction (s .supportAgentStep , {
243- chatId ,
243+ threadId ,
244244 generateText: { prompt },
245245 });
246246 const polished = await step .runAction (s .adaptSuggestionForUser , {
@@ -252,15 +252,15 @@ export const supportAgentWorkflow = workflow.define({
252252});
253253```
254254
255- ### Fetching chat history
255+ ### Fetching thread history
256256
257257``` ts
258- const messages = await ctx .runQuery (components .agent .messages .getChatMessages , {
259- chatId ,
258+ const messages = await ctx .runQuery (components .agent .messages .getThreadMessages , {
259+ threadId ,
260260});
261261```
262262
263- ### Generating text for a user without an associated chat
263+ ### Generating text for a user without an associated thread
264264
265265``` ts
266266const result = await supportAgent .generateText (ctx , { userId }, { prompt });
@@ -269,23 +269,23 @@ const result = await supportAgent.generateText(ctx, { userId }, { prompt });
269269### Manually managing messages
270270
271271``` ts
272- const messages = await ctx .runQuery (components .agent .messages .getChatMessages , {
273- chatId ,
272+ const messages = await ctx .runQuery (components .agent .messages .getThreadMessages , {
273+ threadId ,
274274 {... searchOptions }
275275});
276276```
277277
278278``` ts
279- const messages = await agent .saveMessages (ctx , { chatId , userId , messages });
279+ const messages = await agent .saveMessages (ctx , { threadId , userId , messages });
280280```
281281
282282``` ts
283- const messages = await agent .saveSteps (ctx , { chatId , userId , step });
283+ const messages = await agent .saveSteps (ctx , { threadId , userId , step });
284284```
285285
286286// Update the message from pending to complete, along with any associated steps.
287287``` ts
288- const messages = await agent .completeMessage (ctx , { chatId , userId , messageId });
288+ const messages = await agent .completeMessage (ctx , { threadId , userId , messageId });
289289```
290290
291291### Manage embeddings
@@ -300,10 +300,10 @@ const messages = await ctx.runQuery(components.agent.embeddings.paginate, {
300300```
301301
302302``` ts
303- const messages = await ctx .runQuery (components .agent .embeddings .deleteBatchForChat , {
303+ const messages = await ctx .runQuery (components .agent .embeddings .deleteBatchForThread , {
304304 vectorDimension: 1536 ,
305305 targetModel: " gpt-4o-mini" ,
306- chatId : " 123" ,
306+ threadId : " 123" ,
307307 cursor: null ,
308308 limit: 10 ,
309309});
@@ -315,9 +315,9 @@ const messages = await ctx.runQuery(components.agent.embeddings.insertBatch, {
315315 vectors: [
316316 {
317317 model: " gpt-4o-mini" ,
318- kind: " chat " ,
318+ kind: " thread " ,
319319 userId: " 123" ,
320- chatId : " 123" ,
320+ threadId : " 123" ,
321321 vector: embedding ,
322322 },
323323 ],
0 commit comments