From 6ec0aaa1413be82d09ef3de9c7b27d9d78464a87 Mon Sep 17 00:00:00 2001 From: Tat Dat Duong Date: Tue, 13 May 2025 11:55:43 -0700 Subject: [PATCH] Tests --- jest.config.mjs | 10 ++++------ package.json | 8 ++++---- src/tools/default/email-tools.ts | 8 ++++---- src/tools/default/memory-tools.ts | 10 +++++----- src/utils.ts | 24 +++++++++++++++--------- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/jest.config.mjs b/jest.config.mjs index 126d339..b793b86 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -1,8 +1,7 @@ -/** @type {import('jest').Config} */ +/** @type {import('ts-jest').JestConfigWithTsJest} **/ export default { - preset: "ts-jest/presets/js-with-ts-esm", testEnvironment: "node", - testMatch: ["**/tests-ts/**/*.test.ts"], + testMatch: ["**/tests/**/*.test.ts"], extensionsToTreatAsEsm: [".ts"], transform: { "^.+\\.tsx?$": [ @@ -17,10 +16,9 @@ export default { "^@/(.*)$": "/$1", "^(\\.{1,2}/.*)\\.js$": "$1", }, + transformIgnorePatterns: ["node_modules/(?!(@langchain|langchain|@jest)/)"], - rootDir: "../", - setupFilesAfterEnv: ["/tests-ts/setup.mjs"], + setupFilesAfterEnv: ["/tests/setup.mjs"], testTimeout: 30000, // For LLM calls moduleDirectories: ["node_modules", "src"], - resolver: "jest-ts-webcompat-resolver", }; diff --git a/package.json b/package.json index 42b7d60..203bb8f 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,10 @@ "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "format": "prettier --write .", "agent": "langgraphjs dev", - "test": "NODE_OPTIONS=--experimental-vm-modules jest --config=tests-ts/jest.config.mjs", - "test:hitl": "cross-env NODE_OPTIONS=--experimental-vm-modules AGENT_MODULE=email_assistant_hitl jest --config=tests-ts/jest.config.mjs hitl_testing", - "test:memory": "cross-env NODE_OPTIONS=--experimental-vm-modules AGENT_MODULE=email_assistant_hitl_memory jest --config=tests-ts/jest.config.mjs memory_testing", - "test:base": "cross-env NODE_OPTIONS=--experimental-vm-modules AGENT_MODULE=email_assistant jest --config=tests-ts/jest.config.mjs test_response" + "test": "NODE_OPTIONS=--experimental-vm-modules jest --config=jest.config.mjs", + "test:hitl": "cross-env NODE_OPTIONS=--experimental-vm-modules AGENT_MODULE=email_assistant_hitl jest --config=jest.config.mjs hitl.test.ts", + "test:memory": "cross-env NODE_OPTIONS=--experimental-vm-modules AGENT_MODULE=email_assistant_hitl_memory jest --config=jest.config.mjs memory.test.ts", + "test:base": "cross-env NODE_OPTIONS=--experimental-vm-modules AGENT_MODULE=email_assistant jest --config=jest.config.mjs response.test.ts" }, "dependencies": { "@jest/globals": "^29.7.0", diff --git a/src/tools/default/email-tools.ts b/src/tools/default/email-tools.ts index 0526853..90ce1a5 100644 --- a/src/tools/default/email-tools.ts +++ b/src/tools/default/email-tools.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { DynamicStructuredTool, tool } from "@langchain/core/tools"; +import { tool } from "@langchain/core/tools"; /** * Schema for writing an email @@ -32,7 +32,7 @@ ${content} description: "Write an email draft based on provided information. Use this when the user wants to compose a new email message.", schema: emailSchema, - }, + } ); /** @@ -76,7 +76,7 @@ Recommended action: ${priority === "High" ? "Respond immediately" : "Review when description: "Analyze and categorize an email by importance and type. Use this when evaluating how to handle incoming messages.", schema: triageSchema, - }, + } ); /** @@ -99,5 +99,5 @@ export const Done = tool( description: "Signal that you've completed the current task and no further actions are needed.", schema: doneSchema, - }, + } ); diff --git a/src/tools/default/memory-tools.ts b/src/tools/default/memory-tools.ts index 2152e08..8627104 100644 --- a/src/tools/default/memory-tools.ts +++ b/src/tools/default/memory-tools.ts @@ -20,7 +20,7 @@ export const backgroundTool = tool( name: "background", description: "Get background information about the user", schema: z.object({}).describe("This tool doesn't take any arguments"), - }, + } ); /** @@ -36,7 +36,7 @@ export const calPreferencesTool = tool( name: "cal_preferences", description: "Get the user's calendar preferences", schema: z.object({}).describe("This tool doesn't take any arguments"), - }, + } ); /** @@ -52,14 +52,14 @@ export const responsePreferencesTool = tool( name: "response_preferences", description: "Get the user's response style preferences", schema: z.object({}).describe("This tool doesn't take any arguments"), - }, + } ); /** * Tool to ask a question to the user */ export const questionTool = tool( - async ({ content }: { content: string }) => { + async ({ content }) => { return `The user will see and can answer this question: ${content}`; }, { @@ -68,5 +68,5 @@ export const questionTool = tool( schema: z.object({ content: z.string().describe("The question to ask the user"), }), - }, + } ); diff --git a/src/utils.ts b/src/utils.ts index d4ec2fd..866cebd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,11 @@ import { EmailData } from "./schemas.js"; -import { AIMessage, type BaseMessage } from "@langchain/core/messages"; +import { + AIMessage, + isAIMessage, + coerceMessageLikeToMessage, + type BaseMessage, +} from "@langchain/core/messages"; import type { ToolCall } from "@langchain/core/messages/tool"; /** @@ -53,7 +58,7 @@ export function formatEmailMarkdown( subject: string, author: string, to: string, - emailThread: string, + emailThread: string ): string { return `## Email: ${subject} @@ -201,13 +206,13 @@ export function extractToolCalls(messages: any[]): string[] { // Handle plain objects if (message.tool_calls && Array.isArray(message.tool_calls)) { toolCallNames.push( - ...message.tool_calls.map((call: any) => call.name.toLowerCase()), + ...message.tool_calls.map((call: any) => call.name.toLowerCase()) ); } // Handle class instances with toolCalls property else if ("toolCalls" in message && Array.isArray(message.toolCalls)) { toolCallNames.push( - ...message.toolCalls.map((call: any) => call.name.toLowerCase()), + ...message.toolCalls.map((call: any) => call.name.toLowerCase()) ); } } @@ -222,8 +227,9 @@ export function extractToolCalls(messages: any[]): string[] { */ export function formatMessagesString(messages: BaseMessage[]): string { return messages - .map((message) => { + .map((messageLike) => { let prefix = ""; + const message = coerceMessageLikeToMessage(messageLike); // Determine prefix based on role if ("role" in message && message.role) { @@ -252,13 +258,13 @@ export function formatMessagesString(messages: BaseMessage[]): string { : JSON.stringify(message.content); // Only AIMessage can have tool_calls - if (message._getType() === "ai") { + if (isAIMessage(message)) { const aiMessage = message as AIMessage; // Cast to AIMessage from @langchain/core/messages if (aiMessage.tool_calls && aiMessage.tool_calls.length > 0) { const toolCallsStr = aiMessage.tool_calls .map( (tc: ToolCall) => - `\n Tool: ${tc.name}\n Args: ${JSON.stringify(tc.args, null, 2)}`, + `\n Tool: ${tc.name}\n Args: ${JSON.stringify(tc.args, null, 2)}` ) .join("\n"); content += `\n[Tool Calls: ${toolCallsStr}]`; @@ -277,12 +283,12 @@ export function formatEmailOptional( subject?: string, author?: string, to?: string, - emailThread?: string, + emailThread?: string ): string { return formatEmailMarkdown( subject ?? "No Subject", author ?? "Unknown Sender", to ?? "Unknown Recipient", - emailThread ?? "", + emailThread ?? "" ); }