import type { BaseMessage } from '@langchain/core/messages'; import type { Tool } from '@langchain/core/tools'; import type { OpenAIClient } from '@langchain/openai'; import type { BufferWindowMemory } from '@langchain/classic/memory'; import { isObjectEmpty } from 'n8n-workflow'; import { zodToJsonSchema } from 'zod-to-json-schema'; // Copied from langchain(`langchain/src/tools/convert_to_openai.ts`) // since these functions are not exported /** * Formats a `Tool` instance into a format that is compatible * with OpenAI's ChatCompletionFunctions. It uses the `zodToJsonSchema` * function to convert the schema of the tool into a JSON * schema, which is then used as the parameters for the OpenAI function. */ export function formatToOpenAIFunction( tool: Tool, ): OpenAIClient.Chat.ChatCompletionCreateParams.Function { return { name: tool.name, description: tool.description, parameters: zodToJsonSchema(tool.schema), }; } export function formatToOpenAITool(tool: Tool): OpenAIClient.Chat.ChatCompletionTool { const schema = zodToJsonSchema(tool.schema); return { type: 'function', function: { name: tool.name, description: tool.description, parameters: schema, }, }; } export function formatToOpenAIAssistantTool(tool: Tool): OpenAIClient.Beta.AssistantTool { return { type: 'function', function: { name: tool.name, description: tool.description, parameters: zodToJsonSchema(tool.schema), }, }; } const requireStrict = (schema: any) => { if (!schema.required) { return false; } // when strict:true, Responses API requires `required` to be present and all properties to be included if (schema.properties) { const propertyNames = Object.keys(schema.properties); const somePropertyMissingFromRequired = propertyNames.some( (propertyName) => !schema.required.includes(propertyName), ); const requireStrict = !somePropertyMissingFromRequired; return requireStrict; } return false; }; export function formatToOpenAIResponsesTool(tool: Tool): OpenAIClient.Responses.FunctionTool { const schema = zodToJsonSchema(tool.schema) as any; const strict = requireStrict(schema); // when strict:true, Responses API requires `additionalProperties` either to be true/false or an object with properties const isAdditionalPropertiesEmpty = schema.additionalProperties && typeof schema.additionalProperties === 'object' && isObjectEmpty(schema.additionalProperties); if (isAdditionalPropertiesEmpty && strict) { schema.additionalProperties = false; } return { type: 'function', name: tool.name, parameters: schema, strict, description: tool.description, }; } export async function getChatMessages(memory: BufferWindowMemory): Promise { return (await memory.loadMemoryVariables({}))[memory.memoryKey] as BaseMessage[]; }