@convex-dev/agent
Version:
A agent component for Convex.
1,153 lines • 59.9 kB
TypeScript
import type { FlexibleSchema, IdGenerator, InferSchema, ProviderOptions } from "@ai-sdk/provider-utils";
import type { CallSettings, EmbeddingModel, GenerateObjectResult, GenerateTextResult, LanguageModel, ModelMessage, StepResult, StopCondition, StreamTextResult, ToolSet } from "ai";
import { generateObject, streamObject } from "ai";
import { type GenericDataModel, type PaginationOptions, type PaginationResult } from "convex/server";
import type { MessageDoc, ThreadDoc } from "../component/schema.js";
import type { threadFieldsSupportingPatch } from "../component/threads.js";
import { type VectorDimension } from "../component/vector/tables.js";
import { extractText, isTool } from "../shared.js";
import { type Message, type MessageStatus, type MessageWithMetadata, type ProviderMetadata, type StreamArgs, type Usage } from "../validators.js";
import { createTool } from "./createTool.js";
import { type SaveMessageArgs, type SaveMessagesArgs } from "./messages.js";
import { type StreamingOptions } from "./streaming.js";
import type { ActionCtx, AgentComponent, ContextOptions, DefaultObjectSchema, GenerationOutputMetadata, MaybeCustomCtx, GenerateObjectArgs, ObjectMode, ObjectSchema, Options, RawRequestResponseHandler, RunActionCtx, RunMutationCtx, RunQueryCtx, StorageOptions, StreamingTextArgs, StreamObjectArgs, SyncStreamsReturnValue, TextArgs, Thread, UsageHandler } from "./types.js";
import type { DataModel } from "../component/_generated/dataModel.js";
export { stepCountIs } from "ai";
export { vMessageDoc, vThreadDoc } from "../component/schema.js";
export { deserializeMessage, serializeDataOrUrl, serializeMessage, } from "../mapping.js";
export { vAssistantMessage, vContextOptions, vMessage, vPaginationResult, vProviderMetadata, vStorageOptions, vStreamArgs, vSystemMessage, vToolMessage, vUsage, vUserMessage, } from "../validators.js";
export type { ToolCtx } from "./createTool.js";
export { definePlaygroundAPI, type AgentsFn, type PlaygroundAPI, } from "./definePlaygroundAPI.js";
export { getFile, storeFile } from "./files.js";
export { listMessages, saveMessage, saveMessages, type SaveMessageArgs, type SaveMessagesArgs, } from "./messages.js";
export { fetchContextMessages, filterOutOrphanedToolMessages, } from "./search.js";
export { abortStream, listStreams, syncStreams } from "./streaming.js";
export { createThread, getThreadMetadata, updateThreadMetadata, searchThreadTitles, } from "./threads.js";
export { createTool, extractText, isTool };
export type { AgentComponent, ContextOptions, MessageDoc, ProviderMetadata, RawRequestResponseHandler, StorageOptions, StreamArgs, SyncStreamsReturnValue, Thread, ThreadDoc, Usage, UsageHandler, };
export type Config = {
/**
* The LLM model to use for generating / streaming text and objects.
* e.g.
* import { openai } from "@ai-sdk/openai"
* const myAgent = new Agent(components.agent, {
* languageModel: openai.chat("gpt-4o-mini"),
*/
languageModel?: LanguageModel;
/**
* The model to use for text embeddings. Optional.
* If specified, it will use this for generating vector embeddings
* of chats, and can opt-in to doing vector search for automatic context
* on generateText, etc.
* e.g.
* import { openai } from "@ai-sdk/openai"
* const myAgent = new Agent(components.agent, {
* ...
* textEmbeddingModel: openai.embedding("text-embedding-3-small")
*/
textEmbeddingModel?: EmbeddingModel<string>;
/**
* Options to determine what messages are included as context in message
* generation. To disable any messages automatically being added, pass:
* { recentMessages: 0 }
*/
contextOptions?: ContextOptions;
/**
* Determines whether messages are automatically stored when passed as
* arguments or generated.
*/
storageOptions?: StorageOptions;
/**
* The usage handler to use for this agent.
*/
usageHandler?: UsageHandler;
/**
* Called for each LLM request/response, so you can do things like
* log the raw request body or response headers to a table, or logs.
*/
rawRequestResponseHandler?: RawRequestResponseHandler;
/**
* Default provider options to pass for the LLM calls.
* This can be overridden at each generate/stream callsite on a per-field
* basis. To clear a default setting, you'll need to pass `undefined`.
*/
providerOptions?: ProviderOptions;
/**
* The default settings to use for the LLM calls.
* This can be overridden at each generate/stream callsite on a per-field
* basis. To clear a default setting, you'll need to pass `undefined`.
*/
callSettings?: CallSettings;
};
export declare class Agent<
/**
* You can require that all `ctx` args to generateText & streamText
* have a certain shape by passing a type here.
* e.g.
* ```ts
* const myAgent = new Agent<{ orgId: string }>(...);
* ```
* This is useful if you want to share that type in `createTool`
* e.g.
* ```ts
* type MyCtx = ToolCtx & { orgId: string };
* const myTool = createTool({
* args: z.object({...}),
* description: "...",
* handler: async (ctx: MyCtx, args) => {
* // use ctx.orgId
* },
* });
*/
CustomCtx extends object = object, AgentTools extends ToolSet = any> {
component: AgentComponent;
options: Config & {
/**
* The name for the agent. This will be attributed on each message
* created by this agent.
*/
name: string;
/**
* The LLM model to use for generating / streaming text and objects.
* e.g.
* import { openai } from "@ai-sdk/openai"
* const myAgent = new Agent(components.agent, {
* languageModel: openai.chat("gpt-4o-mini"),
*/
languageModel: LanguageModel;
/**
* The default system prompt to put in each request.
* Override per-prompt by passing the "system" parameter.
*/
instructions?: string;
/**
* Tools that the agent can call out to and get responses from.
* They can be AI SDK tools (import {tool} from "ai")
* or tools that have Convex context
* (import { createTool } from "@convex-dev/agent")
*/
tools?: AgentTools;
/**
* When generating or streaming text with tools available, this
* determines when to stop. Defaults to stepCountIs(1).
*/
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
/**
* @deprecated Use `languageEmbeddingModel` instead.
*/
chat?: LanguageModel;
};
constructor(component: AgentComponent, options: Config & {
/**
* The name for the agent. This will be attributed on each message
* created by this agent.
*/
name: string;
/**
* The LLM model to use for generating / streaming text and objects.
* e.g.
* import { openai } from "@ai-sdk/openai"
* const myAgent = new Agent(components.agent, {
* languageModel: openai.chat("gpt-4o-mini"),
*/
languageModel: LanguageModel;
/**
* The default system prompt to put in each request.
* Override per-prompt by passing the "system" parameter.
*/
instructions?: string;
/**
* Tools that the agent can call out to and get responses from.
* They can be AI SDK tools (import {tool} from "ai")
* or tools that have Convex context
* (import { createTool } from "@convex-dev/agent")
*/
tools?: AgentTools;
/**
* When generating or streaming text with tools available, this
* determines when to stop. Defaults to stepCountIs(1).
*/
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
/**
* @deprecated Use `languageEmbeddingModel` instead.
*/
chat?: LanguageModel;
});
/**
* Start a new thread with the agent. This will have a fresh history, though if
* you pass in a userId you can have it search across other threads for relevant
* messages as context for the LLM calls.
* @param ctx The context of the Convex function. From an action, you can thread
* with the agent. From a mutation, you can start a thread and save the threadId
* to pass to continueThread later.
* @param args The thread metadata.
* @returns The threadId of the new thread and the thread object.
*/
createThread(ctx: RunActionCtx & CustomCtx, args?: {
/**
* The userId to associate with the thread. If not provided, the thread will be
* anonymous.
*/
userId?: string | null;
/**
* The title of the thread. Not currently used for anything.
*/
title?: string;
/**
* The summary of the thread. Not currently used for anything.
*/
summary?: string;
}): Promise<{
threadId: string;
thread: Thread<AgentTools>;
}>;
/**
* Start a new thread with the agent. This will have a fresh history, though if
* you pass in a userId you can have it search across other threads for relevant
* messages as context for the LLM calls.
* @param ctx The context of the Convex function. From a mutation, you can
* start a thread and save the threadId to pass to continueThread later.
* @param args The thread metadata.
* @returns The threadId of the new thread.
*/
createThread(ctx: RunMutationCtx, args?: {
/**
* The userId to associate with the thread. If not provided, the thread will be
* anonymous.
*/
userId?: string | null;
/**
* The title of the thread. Not currently used for anything.
*/
title?: string;
/**
* The summary of the thread. Not currently used for anything.
*/
summary?: string;
}): Promise<{
threadId: string;
}>;
/**
* Continues a thread using this agent. Note: threads can be continued
* by different agents. This is a convenience around calling the various
* generate and stream functions with explicit userId and threadId parameters.
* @param ctx The ctx object passed to the action handler
* @param { threadId, userId }: the thread and user to associate the messages with.
* @returns Functions bound to the userId and threadId on a `{thread}` object.
*/
continueThread(ctx: ActionCtx & CustomCtx, args: {
/**
* The associated thread created by {@link createThread}
*/
threadId: string;
/**
* If supplied, the userId can be used to search across other threads for
* relevant messages from the same user as context for the LLM calls.
*/
userId?: string | null;
}): Promise<{
thread: Thread<AgentTools>;
}>;
start<TOOLS extends ToolSet | undefined, T>(ctx: ActionCtx & CustomCtx,
/**
* These are the arguments you'll pass to the LLM call such as
* `generateText` or `streamText`. This function will look up the context
* and provide functions to save the steps, abort the generation, and more.
* The type of the arguments returned infers from the type of the arguments
* you pass here.
*/
args: T & {
/**
* If provided, this message will be used as the "prompt" for the LLM call,
* instead of the prompt or messages.
* This is useful if you want to first save a user message, then use it as
* the prompt for the LLM call in another call.
*/
promptMessageId?: string;
/**
* The model to use for the LLM calls. This will override the model specified
* in the Agent constructor.
*/
model?: LanguageModel;
/**
* The tools to use for the tool calls. This will override tools specified
* in the Agent constructor or createThread / continueThread.
*/
tools?: TOOLS;
/**
* The single prompt message to use for the LLM call. This will be the
* last message in the context. If it's a string, it will be a user role.
*/
prompt?: string | (ModelMessage | Message)[];
/**
* If provided alongside prompt, the ordering will be:
* 1. system prompt
* 2. search context
* 3. recent messages
* 4. these messages
* 5. prompt messages, including those already on the same `order` as
* the promptMessageId message, if provided.
*/
messages?: (ModelMessage | Message)[];
/**
* This will be the first message in the context, and overrides the
* agent's instructions.
*/
system?: string;
/**
* The abort signal to be passed to the LLM call. If triggered, it will
* mark the pending message as failed. If the generation is asynchronously
* aborted, it will trigger this signal when detected.
*/
abortSignal?: AbortSignal;
_internal?: {
generateId?: IdGenerator;
};
}, options?: Options & {
userId?: string | null;
threadId?: string;
}): Promise<{
args: T & {
system?: string;
model: LanguageModel;
messages: ModelMessage[];
tools?: TOOLS extends undefined ? AgentTools : TOOLS;
} & CallSettings;
order: number;
stepOrder: number;
userId: string | undefined;
promptMessageId: string | undefined;
updateModel: (model: LanguageModel | undefined) => void;
save: <TOOLS extends ToolSet>(toSave: {
step: StepResult<TOOLS>;
} | {
object: GenerateObjectResult<unknown>;
}, createPendingMessage?: boolean) => Promise<void>;
fail: (reason: string) => Promise<void>;
getSavedMessages: () => MessageDoc[];
}>;
/**
* This behaves like {@link generateText} from the "ai" package except that
* it add context based on the userId and threadId and saves the input and
* resulting messages to the thread, if specified.
* Use {@link continueThread} to get a version of this function already scoped
* to a thread (and optionally userId).
* @param ctx The context passed from the action function calling this.
* @param { userId, threadId }: The user and thread to associate the message with
* @param generateTextArgs The arguments to the generateText function, along with extra controls
* for the {@link ContextOptions} and {@link StorageOptions}.
* @returns The result of the generateText function.
*/
generateText<TOOLS extends ToolSet | undefined = undefined, OUTPUT = never, OUTPUT_PARTIAL = never>(ctx: ActionCtx & CustomCtx, threadOpts: {
userId?: string | null;
threadId?: string;
}, generateTextArgs: TextArgs<AgentTools, TOOLS, OUTPUT, OUTPUT_PARTIAL>, options?: Options): Promise<GenerateTextResult<TOOLS extends undefined ? AgentTools : TOOLS, OUTPUT> & GenerationOutputMetadata>;
/**
* This behaves like {@link streamText} from the "ai" package except that
* it add context based on the userId and threadId and saves the input and
* resulting messages to the thread, if specified.
* Use {@link continueThread} to get a version of this function already scoped
* to a thread (and optionally userId).
*/
streamText<TOOLS extends ToolSet | undefined = undefined, OUTPUT = never, PARTIAL_OUTPUT = never>(ctx: ActionCtx & CustomCtx, threadOpts: {
userId?: string | null;
threadId?: string;
},
/**
* The arguments to the streamText function, similar to the ai `streamText` function.
*/
streamTextArgs: StreamingTextArgs<AgentTools, TOOLS, OUTPUT, PARTIAL_OUTPUT>,
/**
* The {@link ContextOptions} and {@link StorageOptions}
* options to use for fetching contextual messages and saving input/output messages.
*/
options?: Options & {
/**
* Whether to save incremental data (deltas) from streaming responses.
* Defaults to false.
* If false, it will not save any deltas to the database.
* If true, it will save deltas with {@link DEFAULT_STREAMING_OPTIONS}.
*
* Regardless of this option, when streaming you are able to use this
* `streamText` function as you would with the "ai" package's version:
* iterating over the text, streaming it over HTTP, etc.
*/
saveStreamDeltas?: boolean | StreamingOptions;
}): Promise<StreamTextResult<TOOLS extends undefined ? AgentTools : TOOLS, PARTIAL_OUTPUT> & GenerationOutputMetadata>;
/**
* This behaves like {@link generateObject} from the "ai" package except that
* it add context based on the userId and threadId and saves the input and
* resulting messages to the thread, if specified.
* Use {@link continueThread} to get a version of this function already scoped
* to a thread (and optionally userId).
*/
generateObject<SCHEMA extends ObjectSchema = DefaultObjectSchema, OUTPUT extends ObjectMode = InferSchema<SCHEMA> extends string ? "enum" : "object", RESULT = OUTPUT extends "array" ? Array<InferSchema<SCHEMA>> : InferSchema<SCHEMA>>(ctx: ActionCtx & CustomCtx, threadOpts: {
userId?: string | null;
threadId?: string;
},
/**
* The arguments to the generateObject function, similar to the ai.generateObject function.
*/
generateObjectArgs: GenerateObjectArgs<SCHEMA, OUTPUT, RESULT>,
/**
* The {@link ContextOptions} and {@link StorageOptions}
* options to use for fetching contextual messages and saving input/output messages.
*/
options?: Options): Promise<GenerateObjectResult<RESULT> & GenerationOutputMetadata>;
/**
* This behaves like `streamObject` from the "ai" package except that
* it add context based on the userId and threadId and saves the input and
* resulting messages to the thread, if specified.
* Use {@link continueThread} to get a version of this function already scoped
* to a thread (and optionally userId).
*/
streamObject<SCHEMA extends ObjectSchema = DefaultObjectSchema, OUTPUT extends ObjectMode = InferSchema<SCHEMA> extends string ? "enum" : "object", RESULT = OUTPUT extends "array" ? Array<InferSchema<SCHEMA>> : InferSchema<SCHEMA>>(ctx: ActionCtx & CustomCtx, threadOpts: {
userId?: string | null;
threadId?: string;
},
/**
* The arguments to the streamObject function, similar to the ai `streamObject` function.
*/
streamObjectArgs: StreamObjectArgs<SCHEMA, OUTPUT, RESULT> & {
/**
* If provided, this message will be used as the "prompt" for the LLM call,
* instead of the prompt or messages.
* This is useful if you want to first save a user message, then use it as
* the prompt for the LLM call in another call.
*/
promptMessageId?: string;
/**
* The model to use for the LLM calls. This will override the model specified
* in the Agent constructor.
*/
model?: LanguageModel;
},
/**
* The {@link ContextOptions} and {@link StorageOptions}
* options to use for fetching contextual messages and saving input/output messages.
*/
options?: Options): Promise<ReturnType<typeof streamObject<SCHEMA, OUTPUT, RESULT>> & GenerationOutputMetadata>;
/**
* Save a message to the thread.
* @param ctx A ctx object from a mutation or action.
* @param args The message and what to associate it with (user / thread)
* You can pass extra metadata alongside the message, e.g. associated fileIds.
* @returns The messageId of the saved message.
*/
saveMessage(ctx: RunMutationCtx, args: SaveMessageArgs & {
/**
* If true, it will not generate embeddings for the message.
* Useful if you're saving messages in a mutation where you can't run `fetch`.
* You can generate them asynchronously by using the scheduler to run an
* action later that calls `agent.generateAndSaveEmbeddings`.
*/
skipEmbeddings?: boolean;
}): Promise<{
messageId: string;
message: {
id?: string | undefined;
userId?: string | undefined;
embeddingId?: string | undefined;
fileIds?: string[] | undefined;
error?: string | undefined;
agentName?: string | undefined;
model?: string | undefined;
provider?: string | undefined;
providerOptions?: Record<string, Record<string, any>> | undefined;
message?: {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "user";
content: string | ({
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "text";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
mimeType?: string | undefined;
type: "image";
image: string | ArrayBuffer;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
filename?: string | undefined;
type: "file";
mimeType: string;
data: string | ArrayBuffer;
})[];
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "assistant";
content: string | ({
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "text";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
filename?: string | undefined;
type: "file";
mimeType: string;
data: string | ArrayBuffer;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
signature?: string | undefined;
state?: "streaming" | "done" | undefined;
type: "reasoning";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "redacted-reasoning";
data: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
providerExecuted?: boolean | undefined;
type: "tool-call";
toolCallId: string;
toolName: string;
args: any;
})[];
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "tool";
content: {
providerOptions?: Record<string, Record<string, any>> | undefined;
args?: any;
providerExecuted?: boolean | undefined;
isError?: boolean | undefined;
experimental_content?: ({
type: "text";
text: string;
} | {
mimeType?: string | undefined;
type: "image";
data: string;
})[] | undefined;
type: "tool-result";
toolCallId: string;
toolName: string;
result: any;
}[];
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "system";
content: string;
} | undefined;
text?: string | undefined;
reasoning?: string | undefined;
usage?: {
reasoningTokens?: number | undefined;
cachedInputTokens?: number | undefined;
promptTokens: number;
completionTokens: number;
totalTokens: number;
} | undefined;
providerMetadata?: Record<string, Record<string, any>> | undefined;
sources?: ({
title?: string | undefined;
type?: "source" | undefined;
providerOptions?: Record<string, Record<string, any>> | undefined;
id: string;
sourceType: "url";
url: string;
} | {
filename?: string | undefined;
providerMetadata?: Record<string, Record<string, any>> | undefined;
id: string;
title: string;
type: "source";
sourceType: "document";
mediaType: string;
})[] | undefined;
warnings?: ({
details?: string | undefined;
type: "unsupported-setting";
setting: string;
} | {
details?: string | undefined;
type: "unsupported-tool";
tool: any;
} | {
type: "other";
message: string;
})[] | undefined;
finishReason?: "length" | "error" | "other" | "stop" | "content-filter" | "tool-calls" | "unknown" | undefined;
reasoningDetails?: ({
providerOptions?: Record<string, Record<string, any>> | undefined;
signature?: string | undefined;
state?: "streaming" | "done" | undefined;
type: "reasoning";
text: string;
} | {
signature?: string | undefined;
type: "text";
text: string;
} | {
type: "redacted";
data: string;
})[] | undefined;
_id: string;
_creationTime: number;
status: "pending" | "success" | "failed";
order: number;
threadId: string;
stepOrder: number;
tool: boolean;
};
}>;
/**
* Explicitly save messages associated with the thread (& user if provided)
* If you have an embedding model set, it will also generate embeddings for
* the messages.
* @param ctx The ctx parameter to a mutation or action.
* @param args The messages and context to save
* @returns
*/
saveMessages(ctx: RunMutationCtx | RunActionCtx, args: SaveMessagesArgs & {
/**
* Skip generating embeddings for the messages. Useful if you're
* saving messages in a mutation where you can't run `fetch`.
* You can generate them asynchronously by using the scheduler to run an
* action later that calls `agent.generateAndSaveEmbeddings`.
*/
skipEmbeddings?: boolean;
}): Promise<{
messages: MessageDoc[];
}>;
/**
* List messages from a thread.
* @param ctx A ctx object from a query, mutation, or action.
* @param args.threadId The thread to list messages from.
* @param args.paginationOpts Pagination options (e.g. via usePaginatedQuery).
* @param args.excludeToolMessages Whether to exclude tool messages.
* False by default.
* @param args.statuses What statuses to include. All by default.
* @returns The MessageDoc's in a format compatible with usePaginatedQuery.
*/
listMessages(ctx: RunQueryCtx, args: {
threadId: string;
paginationOpts: PaginationOptions;
excludeToolMessages?: boolean;
statuses?: MessageStatus[];
}): Promise<PaginationResult<MessageDoc>>;
/**
* A function that handles fetching stream deltas, used with the React hooks
* `useThreadMessages` or `useStreamingThreadMessages`.
* @param ctx A ctx object from a query, mutation, or action.
* @param args.threadId The thread to sync streams for.
* @param args.streamArgs The stream arguments with per-stream cursors.
* @returns The deltas for each stream from their existing cursor.
*/
syncStreams(ctx: RunQueryCtx, args: {
threadId: string;
streamArgs: StreamArgs | undefined;
includeStatuses?: ("streaming" | "finished" | "aborted")[];
}): Promise<SyncStreamsReturnValue | undefined>;
/**
* Fetch the context messages for a thread.
* @param ctx Either a query, mutation, or action ctx.
* If it is not an action context, you can't do text or
* vector search.
* @param args The associated thread, user, message
* @returns
*/
fetchContextMessages(ctx: RunQueryCtx | RunActionCtx, args: {
userId: string | undefined;
threadId: string | undefined;
messages: (ModelMessage | Message)[];
/**
* If provided, it will search for messages up to and including this message.
* Note: if this is far in the past, text and vector search results may be more
* limited, as it's post-filtering the results.
*/
upToAndIncludingMessageId?: string;
contextOptions: ContextOptions | undefined;
}): Promise<MessageDoc[]>;
/**
* Get the metadata for a thread.
* @param ctx A ctx object from a query, mutation, or action.
* @param args.threadId The thread to get the metadata for.
* @returns The metadata for the thread.
*/
getThreadMetadata(ctx: RunQueryCtx, args: {
threadId: string;
}): Promise<ThreadDoc>;
/**
* Update the metadata for a thread.
* @param ctx A ctx object from a mutation or action.
* @param args.threadId The thread to update the metadata for.
* @param args.patch The patch to apply to the thread.
* @returns The updated thread metadata.
*/
updateThreadMetadata(ctx: RunMutationCtx, args: {
threadId: string;
patch: Partial<Pick<ThreadDoc, (typeof threadFieldsSupportingPatch)[number]>>;
}): Promise<ThreadDoc>;
/**
* Get the embeddings for a set of messages.
* @param messages The messages to get the embeddings for.
* @returns The embeddings for the messages.
*/
generateEmbeddings(ctx: RunActionCtx, { userId, threadId, }: {
userId: string | undefined;
threadId: string | undefined;
}, messages: (ModelMessage | Message)[]): Promise<{
vectors: (number[] | null)[];
dimension: VectorDimension;
model: string;
} | undefined>;
/**
* Generate embeddings for a set of messages, and save them to the database.
* It will not generate or save embeddings for messages that already have an
* embedding.
* @param ctx The ctx parameter to an action.
* @param args The messageIds to generate embeddings for.
*/
generateAndSaveEmbeddings(ctx: RunActionCtx, args: {
messageIds: string[];
}): Promise<void>;
_generateAndSaveEmbeddings(ctx: RunActionCtx, messages: MessageDoc[]): Promise<void>;
/**
* Explicitly save a "step" created by the AI SDK.
* @param ctx The ctx argument to a mutation or action.
* @param args The Step generated by the AI SDK.
*/
saveStep<TOOLS extends ToolSet>(ctx: ActionCtx, args: {
userId?: string;
threadId: string;
/**
* The message this step is in response to.
*/
promptMessageId: string;
/**
* The step to save, possibly including multiple tool calls.
*/
step: StepResult<TOOLS>;
/**
* The model used to generate the step.
* Defaults to the chat model for the Agent.
*/
model?: string;
/**
* The provider of the model used to generate the step.
* Defaults to the chat provider for the Agent.
*/
provider?: string;
}): Promise<{
messages: MessageDoc[];
}>;
/**
* Manually save the result of a generateObject call to the thread.
* This happens automatically when using {@link generateObject} or {@link streamObject}
* from the `thread` object created by {@link continueThread} or {@link createThread}.
* @param ctx The context passed from the mutation or action function calling this.
* @param args The arguments to the saveObject function.
*/
saveObject(ctx: ActionCtx, args: {
userId: string | undefined;
threadId: string;
promptMessageId: string;
model: string | undefined;
provider: string | undefined;
result: GenerateObjectResult<unknown>;
metadata?: Omit<MessageWithMetadata, "message">;
}): Promise<{
messages: MessageDoc[];
}>;
/**
* Commit or rollback a message that was pending.
* This is done automatically when saving messages by default.
* If creating pending messages, you can call this when the full "transaction" is done.
* @param ctx The ctx argument to your mutation or action.
* @param args What message to save. Generally the parent message sent into
* the generateText call.
*/
finalizeMessage(ctx: RunMutationCtx, args: {
messageId: string;
result: {
status: "failed";
error: string;
} | {
status: "success";
};
}): Promise<void>;
/**
* Update a message by its id.
* @param ctx The ctx argument to your mutation or action.
* @param args The message fields to update.
*/
updateMessage(ctx: RunMutationCtx, args: {
/** The id of the message to update. */
messageId: string;
patch: {
/** The message to replace the existing message. */
message: ModelMessage | Message;
/** The status to set on the message. */
status: "success" | "error";
/** The error message to set on the message. */
error?: string;
/**
* These will override the fileIds in the message.
* To remove all existing files, pass an empty array.
* If passing in a new message, pass in the fileIds you explicitly want to keep
* from the previous message, as the new files generated from the new message
* will be added to the list.
* If you pass undefined, it will not change the fileIds unless new
* files are generated from the message. In that case, the new fileIds
* will replace the old fileIds.
*/
fileIds?: string[];
};
}): Promise<void>;
/**
* Delete multiple messages by their ids, including their embeddings
* and reduce the refcount of any files they reference.
* @param ctx The ctx argument to your mutation or action.
* @param args The ids of the messages to delete.
*/
deleteMessages(ctx: RunMutationCtx, args: {
messageIds: string[];
}): Promise<void>;
/**
* Delete a single message by its id, including its embedding
* and reduce the refcount of any files it references.
* @param ctx The ctx argument to your mutation or action.
* @param args The id of the message to delete.
*/
deleteMessage(ctx: RunMutationCtx, args: {
messageId: string;
}): Promise<void>;
/**
* Delete a range of messages by their order and step order.
* Each "order" is a set of associated messages in response to the message
* at stepOrder 0.
* The (startOrder, startStepOrder) is inclusive
* and the (endOrder, endStepOrder) is exclusive.
* To delete all messages at "order" 1, you can pass:
* `{ startOrder: 1, endOrder: 2 }`
* To delete a message at step (order=1, stepOrder=1), you can pass:
* `{ startOrder: 1, startStepOrder: 1, endOrder: 1, endStepOrder: 2 }`
* To delete all messages between (1, 1) up to and including (3, 5), you can pass:
* `{ startOrder: 1, startStepOrder: 1, endOrder: 3, endStepOrder: 6 }`
*
* If it cannot do it in one transaction, it returns information you can use
* to resume the deletion.
* e.g.
* ```ts
* let isDone = false;
* let lastOrder = args.startOrder;
* let lastStepOrder = args.startStepOrder ?? 0;
* while (!isDone) {
* // eslint-disable-next-line @typescript-eslint/no-explicit-any
* ({ isDone, lastOrder, lastStepOrder } = await agent.deleteMessageRange(
* ctx,
* {
* threadId: args.threadId,
* startOrder: lastOrder,
* startStepOrder: lastStepOrder,
* endOrder: args.endOrder,
* endStepOrder: args.endStepOrder,
* }
* ));
* }
* ```
* @param ctx The ctx argument to your mutation or action.
* @param args The range of messages to delete.
*/
deleteMessageRange(ctx: RunMutationCtx, args: {
threadId: string;
startOrder: number;
startStepOrder?: number;
endOrder: number;
endStepOrder?: number;
}): Promise<{
isDone: boolean;
lastOrder?: number;
lastStepOrder?: number;
}>;
/**
* Delete a thread and all its messages and streams asynchronously (in batches)
* This uses a mutation to that processes one page and recursively queues the
* next page for deletion.
* @param ctx The ctx argument to your mutation or action.
* @param args The id of the thread to delete and optionally the page size to use for the delete.
*/
deleteThreadAsync(ctx: RunMutationCtx, args: {
threadId: string;
pageSize?: number;
}): Promise<void>;
/**
* Delete a thread and all its messages and streams synchronously.
* This uses an action to iterate through all pages. If the action fails
* partway, it will not automatically restart.
* @param ctx The ctx argument to your action.
* @param args The id of the thread to delete and optionally the page size to use for the delete.
*/
deleteThreadSync(ctx: RunActionCtx, args: {
threadId: string;
pageSize?: number;
}): Promise<void>;
_saveMessagesAndFetchContext<T extends {
prompt?: string | (ModelMessage | Message)[];
messages?: (ModelMessage | Message)[];
system?: string;
promptMessageId?: string;
pendingMessageId?: string;
model?: LanguageModel;
}>(ctx: RunActionCtx, args: T, { userId: argsUserId, threadId, contextOptions, storageOptions, }: {
userId: string | null | undefined;
threadId: string | undefined;
} & Options): Promise<{
args: Extract<T, {
model: LanguageModel;
messages: ModelMessage[];
}> & CallSettings;
userId: string | undefined;
promptMessageId: string | undefined;
pendingMessageId: string | undefined;
order: number | undefined;
stepOrder: number | undefined;
savedMessages: MessageDoc[] | undefined;
}>;
doEmbed(ctx: RunActionCtx, options: {
userId: string | undefined;
threadId: string | undefined;
values: string[];
abortSignal?: AbortSignal;
headers?: Record<string, string>;
}): Promise<{
embeddings: number[][];
}>;
/**
* WORKFLOW UTILITIES
*/
/**
* Create a mutation that creates a thread so you can call it from a Workflow.
* e.g.
* ```ts
* // in convex/foo.ts
* export const createThread = weatherAgent.createThreadMutation();
*
* const workflow = new WorkflowManager(components.workflow);
* export const myWorkflow = workflow.define({
* args: {},
* handler: async (step) => {
* const { threadId } = await step.runMutation(internal.foo.createThread);
* // use the threadId to generate text, object, etc.
* },
* });
* ```
* @returns A mutation that creates a thread.
*/
createThreadMutation(): import("convex/server").RegisteredMutation<"internal", {
userId?: string | undefined;
title?: string | undefined;
summary?: string | undefined;
}, Promise<{
threadId: string;
}>>;
/**
* Create an action out of this agent so you can call it from workflows or other actions
* without a wrapping function.
* @param spec Configuration for the agent acting as an action, including
* {@link ContextOptions}, {@link StorageOptions}, and {@link stopWhen}.
*/
asTextAction<DataModel extends GenericDataModel>(spec: MaybeCustomCtx<CustomCtx, DataModel, AgentTools> & {
/**
* Whether to stream the text.
* If false, it will generate the text in a single call. (default)
* If true or {@link StreamingOptions}, it will stream the text from the LLM
* and save the chunks to the database with the options you specify, or the
* defaults if you pass true.
*/
stream?: boolean | StreamingOptions;
/**
* When to stop generating text.
* Defaults to the {@link Agent["options"].stopWhen} option.
*/
stopWhen?: StopCondition<AgentTools> | Array<StopCondition<AgentTools>>;
} & Options, overrides?: CallSettings): import("convex/server").RegisteredAction<"internal", {
userId?: string | undefined;
threadId?: string | undefined;
providerOptions?: Record<string, Record<string, any>> | undefined;
system?: string | undefined;
messages?: ({
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "user";
content: string | ({
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "text";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
mimeType?: string | undefined;
type: "image";
image: string | ArrayBuffer;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
filename?: string | undefined;
type: "file";
mimeType: string;
data: string | ArrayBuffer;
})[];
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "assistant";
content: string | ({
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "text";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
filename?: string | undefined;
type: "file";
mimeType: string;
data: string | ArrayBuffer;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
signature?: string | undefined;
state?: "streaming" | "done" | undefined;
type: "reasoning";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "redacted-reasoning";
data: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
providerExecuted?: boolean | undefined;
type: "tool-call";
toolCallId: string;
toolName: string;
args: any;
})[];
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "tool";
content: {
providerOptions?: Record<string, Record<string, any>> | undefined;
args?: any;
providerExecuted?: boolean | undefined;
isError?: boolean | undefined;
experimental_content?: ({
type: "text";
text: string;
} | {
mimeType?: string | undefined;
type: "image";
data: string;
})[] | undefined;
type: "tool-result";
toolCallId: string;
toolName: string;
result: any;
}[];
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "system";
content: string;
})[] | undefined;
stream?: boolean | undefined;
toolChoice?: "required" | "none" | "auto" | {
type: "tool";
toolName: string;
} | undefined;
maxSteps?: number | undefined;
experimental_continueSteps?: boolean | undefined;
prompt?: string | undefined;
promptMessageId?: string | undefined;
contextOptions?: {
excludeToolMessages?: boolean | undefined;
recentMessages?: number | undefined;
searchOptions?: {
textSearch?: boolean | undefined;
vectorSearch?: boolean | undefined;
vectorScoreThreshold?: number | undefined;
messageRange?: {
before: number;
after: number;
} | undefined;
limit: number;
} | undefined;
searchOtherThreads?: boolean | undefined;
} | undefined;
storageOptions?: {
saveMessages?: "all" | "none" | "promptAndOutput" | undefined;
} | undefined;
callSettings?: {
headers?: Record<string, string> | undefined;
maxOutputTokens?: number | undefined;
temperature?: number | undefined;
topP?: number | undefined;
topK?: number | undefined;
presencePenalty?: number | undefined;
frequencyPenalty?: number | undefined;
stopSequences?: string[] | undefined;
seed?: number | undefined;
maxRetries?: number | undefined;
} | undefined;
}, Promise<{
text: string;
promptMessageId: string | undefined;
order: number | undefined;
finishReason: import("@ai-sdk/provider").LanguageModelV2FinishReason;
warnings: Promise<import("@ai-sdk/provider").LanguageModelV2CallWarning[] | undefined>;
savedMessageIds: string[];
} | {
text: string;
promptMessageId: string | undefined;
order: number | undefined;
finishReason: import("@ai-sdk/provider").LanguageModelV2FinishReason;
warnings: import("@ai-sdk/provider").LanguageModelV2CallWarning[] | undefined;
savedMessageIds: string[];
}>>;
/**
* Create an action that generates an object out of this agent so you can call
* it from workflows or other actions without a wrapping function.
* @param spec Configuration for the agent acting as an action, including
* the normal parameters to {@link generateObject}, plus {@link ContextOptions}
* and stopWhen.
*/
asObjectAction<T>(objectArgs: Omit<Parameters<typeof generateObject<FlexibleSchema<T>>>[0], "model">, options?: Options & MaybeCustomCtx<CustomCtx, DataModel, AgentTools>): import("convex/server").RegisteredAction<"internal", {
userId?: string | undefined;
threadId?: string | undefined;
providerOptions?: Record<string, Record<string, any>> | undefined;
system?: string | undefined;
messages?: ({
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "user";
content: string | ({
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "text";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
mimeType?: string | undefined;
type: "image";
image: string | ArrayBuffer;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
filename?: string | undefined;
type: "file";
mimeType: string;
data: string | ArrayBuffer;
})[];
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
role: "assistant";
content: string | ({
providerOptions?: Record<string, Record<string, any>> | undefined;
type: "text";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
filename?: string | undefined;
type: "file";
mimeType: string;
data: string | ArrayBuffer;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
signature?: string | undefined;
state?: "streaming" | "done" | undefined;
type: "reasoning";
text: string;
} | {
providerOptions?: Record<string, Record<string, any>> | undefined;
t