@convex-dev/agent
Version:
A agent component for Convex.
106 lines • 4.13 kB
JavaScript
import { validateVectorDimension } from "../component/vector/tables.js";
import { vMessageWithMetadata, } from "../validators.js";
import { serializeMessage } from "../mapping.js";
import { toUIMessages } from "../UIMessages.js";
import { parse } from "convex-helpers/validators";
/**
* List messages from a thread.
* @param ctx A ctx object from a query, mutation, or action.
* @param component The agent component, usually `components.agent`.
* @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.
*/
export async function listMessages(ctx, component, { threadId, paginationOpts, excludeToolMessages, statuses, }) {
if (paginationOpts.numItems === 0) {
return {
page: [],
isDone: true,
continueCursor: paginationOpts.cursor ?? "",
};
}
return ctx.runQuery(component.messages.listMessagesByThreadId, {
order: "desc",
threadId,
paginationOpts,
excludeToolMessages,
statuses,
});
}
export async function listUIMessages(ctx, component, args) {
const result = await listMessages(ctx, component, args);
return { ...result, page: toUIMessages(result.page) };
}
/**
* Explicitly save messages associated with the thread (& user if provided)
*/
export async function saveMessages(ctx, component, args) {
let embeddings;
if (args.embeddings) {
const dimension = args.embeddings.vectors.find((v) => v !== null)?.length;
if (dimension) {
validateVectorDimension(dimension);
embeddings = {
model: args.embeddings.model,
dimension,
vectors: args.embeddings.vectors,
};
}
}
const result = await ctx.runMutation(component.messages.addMessages, {
threadId: args.threadId,
userId: args.userId ?? undefined,
agentName: args.agentName,
promptMessageId: args.promptMessageId,
pendingMessageId: args.pendingMessageId,
embeddings,
messages: await Promise.all(args.messages.map(async (m, i) => {
const { message, fileIds } = await serializeMessage(ctx, component, m);
const base = args.metadata?.[i];
const allFileIds = [...(base?.fileIds ?? [])];
if (fileIds)
allFileIds.push(...fileIds);
return parse(vMessageWithMetadata, {
...base,
message,
...(allFileIds.length > 0 ? { fileIds: allFileIds } : {}),
});
})),
failPendingSteps: args.failPendingSteps ?? false,
});
return { messages: result.messages };
}
/**
* 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.
*/
export async function saveMessage(ctx, component, args) {
let embeddings;
if (args.embedding && args.embedding.vector) {
embeddings = {
model: args.embedding.model,
vectors: [args.embedding.vector],
};
}
const { messages } = await saveMessages(ctx, component, {
threadId: args.threadId,
userId: args.userId ?? undefined,
agentName: args.agentName,
promptMessageId: args.promptMessageId,
pendingMessageId: args.pendingMessageId,
messages: args.prompt !== undefined
? [{ role: "user", content: args.prompt }]
: [args.message],
metadata: args.metadata ? [args.metadata] : undefined,
embeddings,
});
const message = messages.at(-1);
return { messageId: message._id, message };
}
//# sourceMappingURL=messages.js.map