@convex-dev/agent
Version:
A agent component for Convex.
285 lines (265 loc) • 8.75 kB
text/typescript
/// <reference types="vite/client" />
import { convexTest } from "convex-test";
import { describe, expect, test } from "vitest";
import { api } from "./_generated/api.js";
import type { Id } from "./_generated/dataModel.js";
import { getMaxMessage } from "./messages.js";
import schema from "./schema.js";
import { modules } from "./setup.test.js";
describe("agent", () => {
test("getMaxMessage works for threads", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [
{ message: { role: "user", content: "hello" } },
{ message: { role: "assistant", content: "world" } },
],
});
const maxMessage = await t.run(async (ctx) => {
return await getMaxMessage(ctx, thread._id as Id<"threads">);
});
expect(maxMessage).toMatchObject({
_id: messages.at(-1)!._id,
order: 0,
stepOrder: 1,
});
});
test("getMaxMessages works when there are tools involved", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [
{ message: { role: "user", content: "hello" } },
{
message: {
role: "assistant",
content: [
{
type: "tool-call",
args: { a: 1 },
toolCallId: "1",
toolName: "tool",
},
],
},
},
{
message: {
role: "tool",
content: [
{
type: "tool-result",
toolName: "tool",
result: "foo",
toolCallId: "1",
},
],
},
},
{ message: { role: "assistant", content: "world" } },
],
});
const maxMessage = await t.run(async (ctx) => {
return await getMaxMessage(ctx, thread._id as Id<"threads">);
});
expect(maxMessage).toMatchObject({
_id: messages.at(-1)!._id,
order: 0,
stepOrder: 3,
});
});
test("ordering is incremented on subsequent calls to addMessages", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{ message: { role: "user", content: "hello" } }],
});
const maxMessage = await t.run(async (ctx) => {
return await getMaxMessage(ctx, thread._id as Id<"threads">);
});
expect(maxMessage).toMatchObject({
_id: messages.at(-1)!._id,
order: 0,
stepOrder: 0,
});
const { messages: messages2 } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{ message: { role: "user", content: "hello" } }],
});
const maxMessage2 = await t.run(async (ctx) => {
return await getMaxMessage(ctx, thread._id as Id<"threads">);
});
expect(maxMessage2).toMatchObject({
_id: messages2.at(-1)!._id,
order: 1,
stepOrder: 0,
});
});
test("sub order is incremented on subsequent calls to addMessages for the same promptMessageId", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{ message: { role: "user", content: "hello" } }],
});
const maxMessage = await t.run(async (ctx) => {
return await getMaxMessage(ctx, thread._id as Id<"threads">);
});
expect(maxMessage).toMatchObject({
_id: messages.at(-1)!._id,
order: 0,
stepOrder: 0,
});
const { messages: messages2 } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{ message: { role: "user", content: "hello" } }],
agentName: "test",
promptMessageId: messages.at(-1)!._id as Id<"messages">,
});
const maxMessage2 = await t.run(async (ctx) => {
return await getMaxMessage(ctx, thread._id as Id<"threads">);
});
expect(maxMessage2).toMatchObject({
_id: messages2.at(-1)!._id,
order: 0,
stepOrder: 1,
});
});
test("updateMessage updates message content", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{ message: { role: "user", content: "hello" } }],
});
const messageId = messages[0]._id as Id<"messages">;
const updatedMessage = await t.mutation(api.messages.updateMessage, {
messageId,
patch: {
message: { role: "user", content: "updated content" },
},
});
expect(updatedMessage.message).toEqual({
role: "user",
content: "updated content",
});
});
test("updateMessage updates message status", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{ message: { role: "assistant", content: "hello" } }],
pending: true,
});
const messageId = messages[0]._id as Id<"messages">;
// Initial status should be pending
expect(messages[0].status).toBe("pending");
// Update to success
const updatedMessage = await t.mutation(api.messages.updateMessage, {
messageId,
patch: {
status: "success",
},
});
expect(updatedMessage.status).toBe("success");
});
test("updateMessage updates error field", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{ message: { role: "assistant", content: "hello" } }],
pending: true,
});
const messageId = messages[0]._id as Id<"messages">;
const updatedMessage = await t.mutation(api.messages.updateMessage, {
messageId,
patch: {
status: "failed",
error: "Something went wrong",
},
});
expect(updatedMessage.status).toBe("failed");
expect(updatedMessage.error).toBe("Something went wrong");
});
test("updateMessage correctly updates tool messages", async () => {
const t = convexTest(schema, modules);
const thread = await t.mutation(api.threads.createThread, {
userId: "test",
});
const { messages } = await t.mutation(api.messages.addMessages, {
threadId: thread._id as Id<"threads">,
messages: [{
message: {
role: "assistant",
content: [
{
type: "tool-call",
args: { a: 1 },
toolCallId: "1",
toolName: "tool",
},
],
},
}],
});
const messageId = messages[0]._id as Id<"messages">;
const updatedMessage = await t.mutation(api.messages.updateMessage, {
messageId,
patch: {
message: {
role: "assistant",
content: [
{
type: "tool-call",
args: { a: 2, b: 3 },
toolCallId: "1",
toolName: "tool",
},
],
},
},
});
expect(updatedMessage.message).toEqual({
role: "assistant",
content: [
{
type: "tool-call",
args: { a: 2, b: 3 },
toolCallId: "1",
toolName: "tool",
},
],
});
});
test("updateMessage throws error for non-existent message", async () => {
const t = convexTest(schema, modules);
await expect(
t.mutation(api.messages.updateMessage, {
messageId: "invalidId" as Id<"messages">,
patch: {
message: { role: "user", content: "test" },
},
})
).rejects.toThrow();
});
});