From 6a2f9e72959fb0e89006b69c31fbcee1ead1cde9 Mon Sep 17 00:00:00 2001 From: oleg Date: Mon, 7 Oct 2024 09:58:28 +0200 Subject: [PATCH] feat(OpenAI Node): Allow to specify thread ID for Assistant -> Message operation (#11080) --- .../actions/assistant/message.operation.ts | 90 ++++++++++++++----- .../OpenAi/actions/versionDescription.ts | 20 +++-- 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts index 3ec2e46eea..cf770e4057 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts @@ -1,30 +1,27 @@ +import type { BaseMessage } from '@langchain/core/messages'; import { AgentExecutor } from 'langchain/agents'; - -import { OpenAIAssistantRunnable } from 'langchain/experimental/openai_assistant'; import type { OpenAIToolType } from 'langchain/dist/experimental/openai_assistant/schema'; -import { OpenAI as OpenAIClient } from 'openai'; - -import { - ApplicationError, - NodeConnectionType, - NodeOperationError, - updateDisplayOptions, -} from 'n8n-workflow'; +import { OpenAIAssistantRunnable } from 'langchain/experimental/openai_assistant'; +import type { BufferWindowMemory } from 'langchain/memory'; +import omit from 'lodash/omit'; import type { IDataObject, IExecuteFunctions, INodeExecutionData, INodeProperties, } from 'n8n-workflow'; - -import type { BufferWindowMemory } from 'langchain/memory'; -import omit from 'lodash/omit'; -import type { BaseMessage } from '@langchain/core/messages'; -import { formatToOpenAIAssistantTool } from '../../helpers/utils'; -import { assistantRLC } from '../descriptions'; +import { + ApplicationError, + NodeConnectionType, + NodeOperationError, + updateDisplayOptions, +} from 'n8n-workflow'; +import { OpenAI as OpenAIClient } from 'openai'; import { getConnectedTools } from '../../../../../utils/helpers'; import { getTracingConfig } from '../../../../../utils/tracing'; +import { formatToOpenAIAssistantTool } from '../../helpers/utils'; +import { assistantRLC } from '../descriptions'; const properties: INodeProperties[] = [ assistantRLC, @@ -63,6 +60,46 @@ const properties: INodeProperties[] = [ }, }, }, + { + displayName: 'Memory', + name: 'memory', + type: 'options', + options: [ + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'Use memory connector', + value: 'connector', + description: 'Connect one of the supported memory nodes', + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'Use thread ID', + value: 'threadId', + description: 'Specify the ID of the thread to continue', + }, + ], + displayOptions: { + show: { + '@version': [{ _cnd: { gte: 1.6 } }], + }, + }, + default: 'connector', + }, + { + displayName: 'Thread ID', + name: 'threadId', + type: 'string', + default: '', + placeholder: '', + description: 'The ID of the thread to continue, a new thread will be created if not specified', + hint: 'If the thread ID is empty or undefined a new thread will be created and included in the response', + displayOptions: { + show: { + '@version': [{ _cnd: { gte: 1.6 } }], + memory: ['threadId'], + }, + }, + }, { displayName: 'Connect your own custom n8n tools to this node on the canvas', name: 'noticeTools', @@ -201,9 +238,19 @@ export async function execute(this: IExecuteFunctions, i: number): Promise= 1.6 && this.getNodeParameter('memory', i) === 'connector'; + const memory = + useMemoryConnector || nodeVersion < 1.6 + ? ((await this.getInputConnectionData(NodeConnectionType.AiMemory, 0)) as + | BufferWindowMemory + | undefined) + : undefined; + + const threadId = + nodeVersion >= 1.6 && !useMemoryConnector + ? (this.getNodeParameter('threadId', i) as string) + : undefined; const chainValues: IDataObject = { content: input, @@ -231,6 +278,8 @@ export async function execute(this: IExecuteFunctions, i: number): Promise { return `${capitalize(operation)} ${capitalize(resource)}`; }; -const configureNodeInputs = (resource: string, operation: string, hideTools: string) => { +const configureNodeInputs = ( + resource: string, + operation: string, + hideTools: string, + memory: string | undefined, +) => { if (resource === 'assistant' && operation === 'message') { - return [ + const inputs: INodeInputConfiguration[] = [ { type: NodeConnectionType.Main }, - { type: NodeConnectionType.AiMemory, displayName: 'Memory', maxConnections: 1 }, { type: NodeConnectionType.AiTool, displayName: 'Tools' }, ]; + if (memory !== 'threadId') { + inputs.push({ type: NodeConnectionType.AiMemory, displayName: 'Memory', maxConnections: 1 }); + } + return inputs; } if (resource === 'text' && operation === 'message') { if (hideTools === 'hide') { @@ -69,7 +77,7 @@ export const versionDescription: INodeTypeDescription = { name: 'openAi', icon: { light: 'file:openAi.svg', dark: 'file:openAi.dark.svg' }, group: ['transform'], - version: [1, 1.1, 1.2, 1.3, 1.4, 1.5], + version: [1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6], subtitle: `={{(${prettifyOperation})($parameter.resource, $parameter.operation)}}`, description: 'Message an assistant or GPT, analyze images, generate audio, etc.', defaults: { @@ -89,7 +97,7 @@ export const versionDescription: INodeTypeDescription = { ], }, }, - inputs: `={{(${configureNodeInputs})($parameter.resource, $parameter.operation, $parameter.hideTools)}}`, + inputs: `={{(${configureNodeInputs})($parameter.resource, $parameter.operation, $parameter.hideTools, $parameter.memory ?? undefined)}}`, outputs: [NodeConnectionType.Main], credentials: [ {