From 565f8cd8c78b534a50e272997d659d162fa86625 Mon Sep 17 00:00:00 2001 From: oleg Date: Tue, 12 Nov 2024 11:33:20 +0100 Subject: [PATCH] feat(editor): Improve how we show default Agent prompt and Memory session parameters (#11491) --- .../nodes/agents/Agent/Agent.node.ts | 13 ++++- .../Agent/agents/SqlAgent/description.ts | 13 ++++- .../agents/Agent/agents/SqlAgent/execute.ts | 21 ++++---- .../OpenAiAssistant/OpenAiAssistant.node.ts | 7 +-- .../nodes/chains/ChainLLM/ChainLlm.node.ts | 27 +++------- .../ChainRetrievalQA/ChainRetrievalQa.node.ts | 49 +++++++------------ .../MemoryBufferWindow.node.ts | 17 +++++-- .../MemoryMotorhead/MemoryMotorhead.node.ts | 9 ++-- .../MemoryPostgresChat.node.ts | 23 ++++++--- .../MemoryRedisChat/MemoryRedisChat.node.ts | 19 ++++--- .../memory/MemoryXata/MemoryXata.node.ts | 19 ++++--- .../nodes/memory/MemoryZep/MemoryZep.node.ts | 5 +- .../nodes/memory/descriptions.ts | 14 ++++++ .../actions/assistant/message.operation.ts | 27 +++++----- .../OpenAi/actions/versionDescription.ts | 2 +- .../nodes-langchain/utils/descriptions.ts | 14 +++++- .../ExpressionEditorModalInput.vue | 14 ++++-- .../InlineExpressionEditorInput.vue | 14 +++++- .../InlineExpressionEditorOutput.vue | 4 +- .../src/components/ParameterInput.vue | 4 +- .../src/components/ParameterInputFull.vue | 12 ++++- .../src/components/ParameterInputList.vue | 24 ++++++--- .../src/components/ParameterOptions.vue | 2 +- .../src/composables/useNodeHelpers.ts | 3 +- packages/workflow/src/Interfaces.ts | 2 + packages/workflow/src/NodeHelpers.ts | 8 +-- 26 files changed, 231 insertions(+), 135 deletions(-) diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts index 0b27456db6c35..80e5da9cfacce 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts @@ -21,7 +21,7 @@ import { sqlAgentAgentProperties } from './agents/SqlAgent/description'; import { sqlAgentAgentExecute } from './agents/SqlAgent/execute'; import { toolsAgentProperties } from './agents/ToolsAgent/description'; import { toolsAgentExecute } from './agents/ToolsAgent/execute'; -import { promptTypeOptions, textInput } from '../../../utils/descriptions'; +import { promptTypeOptions, textFromPreviousNode, textInput } from '../../../utils/descriptions'; // Function used in the inputs expression to figure out which inputs to // display based on the agent type @@ -341,6 +341,17 @@ export class Agent implements INodeType { }, }, }, + { + ...textFromPreviousNode, + displayOptions: { + show: { promptType: ['auto'], '@version': [{ _cnd: { gte: 1.7 } }] }, + // SQL Agent has data source and credentials parameters so we need to include this input there manually + // to preserve the order + hide: { + agent: ['sqlAgent'], + }, + }, + }, { ...textInput, displayOptions: { diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/description.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/description.ts index c7f888a334903..bed547ba6da2f 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/description.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/description.ts @@ -1,6 +1,11 @@ import type { INodeProperties } from 'n8n-workflow'; -import { promptTypeOptions, textInput } from '../../../../../utils/descriptions'; + import { SQL_PREFIX, SQL_SUFFIX } from './other/prompts'; +import { + promptTypeOptions, + textFromPreviousNode, + textInput, +} from '../../../../../utils/descriptions'; const dataSourceOptions: INodeProperties = { displayName: 'Data Source', @@ -114,6 +119,12 @@ export const sqlAgentAgentProperties: INodeProperties[] = [ }, }, }, + { + ...textFromPreviousNode, + displayOptions: { + show: { promptType: ['auto'], '@version': [{ _cnd: { gte: 1.7 } }], agent: ['sqlAgent'] }, + }, + }, { ...textInput, displayOptions: { diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts index 4b31491120af0..b9c0f3db8ee9b 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts @@ -1,3 +1,9 @@ +import type { BaseChatMemory } from '@langchain/community/memory/chat_memory'; +import type { BaseLanguageModel } from '@langchain/core/language_models/base'; +import type { DataSource } from '@n8n/typeorm'; +import type { SqlCreatePromptArgs } from 'langchain/agents/toolkits/sql'; +import { SqlToolkit, createSqlAgent } from 'langchain/agents/toolkits/sql'; +import { SqlDatabase } from 'langchain/sql_db'; import { type IExecuteFunctions, type INodeExecutionData, @@ -6,19 +12,12 @@ import { type IDataObject, } from 'n8n-workflow'; -import { SqlDatabase } from 'langchain/sql_db'; -import type { SqlCreatePromptArgs } from 'langchain/agents/toolkits/sql'; -import { SqlToolkit, createSqlAgent } from 'langchain/agents/toolkits/sql'; -import type { BaseLanguageModel } from '@langchain/core/language_models/base'; -import type { BaseChatMemory } from '@langchain/community/memory/chat_memory'; -import type { DataSource } from '@n8n/typeorm'; - -import { getPromptInputByType, serializeChatHistory } from '../../../../../utils/helpers'; -import { getTracingConfig } from '../../../../../utils/tracing'; -import { getSqliteDataSource } from './other/handlers/sqlite'; +import { getMysqlDataSource } from './other/handlers/mysql'; import { getPostgresDataSource } from './other/handlers/postgres'; +import { getSqliteDataSource } from './other/handlers/sqlite'; import { SQL_PREFIX, SQL_SUFFIX } from './other/prompts'; -import { getMysqlDataSource } from './other/handlers/mysql'; +import { getPromptInputByType, serializeChatHistory } from '../../../../../utils/helpers'; +import { getTracingConfig } from '../../../../../utils/tracing'; const parseTablesString = (tablesString: string) => tablesString diff --git a/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts b/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts index d8e6f8a775d94..ca42323fc336c 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts @@ -1,5 +1,5 @@ import { AgentExecutor } from 'langchain/agents'; -import { OpenAI as OpenAIClient } from 'openai'; +import type { OpenAIToolType } from 'langchain/dist/experimental/openai_assistant/schema'; import { OpenAIAssistantRunnable } from 'langchain/experimental/openai_assistant'; import { NodeConnectionType, NodeOperationError } from 'n8n-workflow'; import type { @@ -8,10 +8,11 @@ import type { INodeType, INodeTypeDescription, } from 'n8n-workflow'; -import type { OpenAIToolType } from 'langchain/dist/experimental/openai_assistant/schema'; +import { OpenAI as OpenAIClient } from 'openai'; + +import { formatToOpenAIAssistantTool } from './utils'; import { getConnectedTools } from '../../../utils/helpers'; import { getTracingConfig } from '../../../utils/tracing'; -import { formatToOpenAIAssistantTool } from './utils'; export class OpenAiAssistant implements INodeType { description: INodeTypeDescription = { diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts index a080862c0fd7e..32d70f2d3208d 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts @@ -36,6 +36,7 @@ import { getCustomErrorMessage as getCustomOpenAiErrorMessage, isOpenAiError, } from '../../vendors/OpenAi/helpers/error-handling'; +import { promptTypeOptions, textFromPreviousNode } from '../../../utils/descriptions'; interface MessagesTemplate { type: string; @@ -253,7 +254,7 @@ export class ChainLlm implements INodeType { name: 'chainLlm', icon: 'fa:link', group: ['transform'], - version: [1, 1.1, 1.2, 1.3, 1.4], + version: [1, 1.1, 1.2, 1.3, 1.4, 1.5], description: 'A simple chain to prompt a large language model', defaults: { name: 'Basic LLM Chain', @@ -315,30 +316,16 @@ export class ChainLlm implements INodeType { }, }, { - displayName: 'Prompt', - name: 'promptType', - type: 'options', - options: [ - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'Take from previous node automatically', - value: 'auto', - description: 'Looks for an input field called chatInput', - }, - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'Define below', - value: 'define', - description: - 'Use an expression to reference data in previous nodes or enter static text', - }, - ], + ...promptTypeOptions, displayOptions: { hide: { '@version': [1, 1.1, 1.2, 1.3], }, }, - default: 'auto', + }, + { + ...textFromPreviousNode, + displayOptions: { show: { promptType: ['auto'], '@version': [{ _cnd: { gte: 1.5 } }] } }, }, { displayName: 'Text', diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts index dfbdb3e9d19e4..75f7458438440 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts @@ -1,3 +1,12 @@ +import type { BaseLanguageModel } from '@langchain/core/language_models/base'; +import { + ChatPromptTemplate, + SystemMessagePromptTemplate, + HumanMessagePromptTemplate, + PromptTemplate, +} from '@langchain/core/prompts'; +import type { BaseRetriever } from '@langchain/core/retrievers'; +import { RetrievalQAChain } from 'langchain/chains'; import { NodeConnectionType, type IExecuteFunctions, @@ -7,20 +16,12 @@ import { NodeOperationError, } from 'n8n-workflow'; -import { RetrievalQAChain } from 'langchain/chains'; -import type { BaseLanguageModel } from '@langchain/core/language_models/base'; -import type { BaseRetriever } from '@langchain/core/retrievers'; -import { - ChatPromptTemplate, - SystemMessagePromptTemplate, - HumanMessagePromptTemplate, - PromptTemplate, -} from '@langchain/core/prompts'; -import { getTemplateNoticeField } from '../../../utils/sharedFields'; +import { promptTypeOptions, textFromPreviousNode } from '../../../utils/descriptions'; import { getPromptInputByType, isChatInstance } from '../../../utils/helpers'; +import { getTemplateNoticeField } from '../../../utils/sharedFields'; import { getTracingConfig } from '../../../utils/tracing'; -const SYSTEM_PROMPT_TEMPLATE = `Use the following pieces of context to answer the users question. +const SYSTEM_PROMPT_TEMPLATE = `Use the following pieces of context to answer the users question. If you don't know the answer, just say that you don't know, don't try to make up an answer. ---------------- {context}`; @@ -31,7 +32,7 @@ export class ChainRetrievalQa implements INodeType { name: 'chainRetrievalQa', icon: 'fa:link', group: ['transform'], - version: [1, 1.1, 1.2, 1.3], + version: [1, 1.1, 1.2, 1.3, 1.4], description: 'Answer questions about retrieved documents', defaults: { name: 'Question and Answer Chain', @@ -108,30 +109,16 @@ export class ChainRetrievalQa implements INodeType { }, }, { - displayName: 'Prompt', - name: 'promptType', - type: 'options', - options: [ - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'Take from previous node automatically', - value: 'auto', - description: 'Looks for an input field called chatInput', - }, - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'Define below', - value: 'define', - description: - 'Use an expression to reference data in previous nodes or enter static text', - }, - ], + ...promptTypeOptions, displayOptions: { hide: { '@version': [{ _cnd: { lte: 1.2 } }], }, }, - default: 'auto', + }, + { + ...textFromPreviousNode, + displayOptions: { show: { promptType: ['auto'], '@version': [{ _cnd: { gte: 1.4 } }] } }, }, { displayName: 'Text', diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts index fae6927c25099..28025f28845e9 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts @@ -1,4 +1,6 @@ /* eslint-disable n8n-nodes-base/node-dirname-against-convention */ +import type { BufferWindowMemoryInput } from 'langchain/memory'; +import { BufferWindowMemory } from 'langchain/memory'; import { NodeConnectionType, type INodeType, @@ -6,12 +8,16 @@ import { type ISupplyDataFunctions, type SupplyData, } from 'n8n-workflow'; -import type { BufferWindowMemoryInput } from 'langchain/memory'; -import { BufferWindowMemory } from 'langchain/memory'; + +import { getSessionId } from '../../../utils/helpers'; import { logWrapper } from '../../../utils/logWrapper'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; -import { sessionIdOption, sessionKeyProperty, contextWindowLengthProperty } from '../descriptions'; -import { getSessionId } from '../../../utils/helpers'; +import { + sessionIdOption, + sessionKeyProperty, + contextWindowLengthProperty, + expressionSessionKeyProperty, +} from '../descriptions'; class MemoryChatBufferSingleton { private static instance: MemoryChatBufferSingleton; @@ -72,7 +78,7 @@ export class MemoryBufferWindow implements INodeType { name: 'memoryBufferWindow', icon: 'fa:database', group: ['transform'], - version: [1, 1.1, 1.2], + version: [1, 1.1, 1.2, 1.3], description: 'Stores in n8n memory, so no credentials required', defaults: { name: 'Window Buffer Memory', @@ -129,6 +135,7 @@ export class MemoryBufferWindow implements INodeType { }, }, }, + expressionSessionKeyProperty(1.3), sessionKeyProperty, contextWindowLengthProperty, ], diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts index 7f8d88fbaf7ea..a326f4c1bea4c 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts @@ -1,4 +1,5 @@ /* eslint-disable n8n-nodes-base/node-dirname-against-convention */ +import { MotorheadMemory } from '@langchain/community/memory/motorhead_memory'; import { NodeConnectionType, type INodeType, @@ -7,11 +8,10 @@ import { type SupplyData, } from 'n8n-workflow'; -import { MotorheadMemory } from '@langchain/community/memory/motorhead_memory'; +import { getSessionId } from '../../../utils/helpers'; import { logWrapper } from '../../../utils/logWrapper'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; -import { sessionIdOption, sessionKeyProperty } from '../descriptions'; -import { getSessionId } from '../../../utils/helpers'; +import { expressionSessionKeyProperty, sessionIdOption, sessionKeyProperty } from '../descriptions'; export class MemoryMotorhead implements INodeType { description: INodeTypeDescription = { @@ -19,7 +19,7 @@ export class MemoryMotorhead implements INodeType { name: 'memoryMotorhead', icon: 'fa:file-export', group: ['transform'], - version: [1, 1.1, 1.2], + version: [1, 1.1, 1.2, 1.3], description: 'Use Motorhead Memory', defaults: { name: 'Motorhead', @@ -82,6 +82,7 @@ export class MemoryMotorhead implements INodeType { }, }, }, + expressionSessionKeyProperty(1.3), sessionKeyProperty, ], }; diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryPostgresChat/MemoryPostgresChat.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryPostgresChat/MemoryPostgresChat.node.ts index f51b76fb183dc..b3d5f2f409128 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryPostgresChat/MemoryPostgresChat.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryPostgresChat/MemoryPostgresChat.node.ts @@ -1,4 +1,9 @@ /* eslint-disable n8n-nodes-base/node-dirname-against-convention */ +import { PostgresChatMessageHistory } from '@langchain/community/stores/message/postgres'; +import { BufferMemory, BufferWindowMemory } from 'langchain/memory'; +import type { PostgresNodeCredentials } from 'n8n-nodes-base/dist/nodes/Postgres/v2/helpers/interfaces'; +import { postgresConnectionTest } from 'n8n-nodes-base/dist/nodes/Postgres/v2/methods/credentialTest'; +import { configurePostgres } from 'n8n-nodes-base/dist/nodes/Postgres/v2/transport'; import type { ISupplyDataFunctions, INodeType, @@ -6,16 +11,17 @@ import type { SupplyData, } from 'n8n-workflow'; import { NodeConnectionType } from 'n8n-workflow'; -import { BufferMemory, BufferWindowMemory } from 'langchain/memory'; -import { PostgresChatMessageHistory } from '@langchain/community/stores/message/postgres'; import type pg from 'pg'; -import { configurePostgres } from 'n8n-nodes-base/dist/nodes/Postgres/v2/transport'; -import type { PostgresNodeCredentials } from 'n8n-nodes-base/dist/nodes/Postgres/v2/helpers/interfaces'; -import { postgresConnectionTest } from 'n8n-nodes-base/dist/nodes/Postgres/v2/methods/credentialTest'; + +import { getSessionId } from '../../../utils/helpers'; import { logWrapper } from '../../../utils/logWrapper'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; -import { sessionIdOption, sessionKeyProperty, contextWindowLengthProperty } from '../descriptions'; -import { getSessionId } from '../../../utils/helpers'; +import { + sessionIdOption, + sessionKeyProperty, + contextWindowLengthProperty, + expressionSessionKeyProperty, +} from '../descriptions'; export class MemoryPostgresChat implements INodeType { description: INodeTypeDescription = { @@ -23,7 +29,7 @@ export class MemoryPostgresChat implements INodeType { name: 'memoryPostgresChat', icon: 'file:postgres.svg', group: ['transform'], - version: [1, 1.1], + version: [1, 1.1, 1.2, 1.3], description: 'Stores the chat history in Postgres table.', defaults: { name: 'Postgres Chat Memory', @@ -56,6 +62,7 @@ export class MemoryPostgresChat implements INodeType { properties: [ getConnectionHintNoticeField([NodeConnectionType.AiAgent]), sessionIdOption, + expressionSessionKeyProperty(1.2), sessionKeyProperty, { displayName: 'Table Name', diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryRedisChat/MemoryRedisChat.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryRedisChat/MemoryRedisChat.node.ts index 01a31458de3bc..23c7f64e36715 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryRedisChat/MemoryRedisChat.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryRedisChat/MemoryRedisChat.node.ts @@ -1,4 +1,7 @@ /* eslint-disable n8n-nodes-base/node-dirname-against-convention */ +import type { RedisChatMessageHistoryInput } from '@langchain/redis'; +import { RedisChatMessageHistory } from '@langchain/redis'; +import { BufferMemory, BufferWindowMemory } from 'langchain/memory'; import { NodeOperationError, type INodeType, @@ -7,15 +10,18 @@ import { type SupplyData, NodeConnectionType, } from 'n8n-workflow'; -import { BufferMemory, BufferWindowMemory } from 'langchain/memory'; -import type { RedisChatMessageHistoryInput } from '@langchain/redis'; -import { RedisChatMessageHistory } from '@langchain/redis'; import type { RedisClientOptions } from 'redis'; import { createClient } from 'redis'; + +import { getSessionId } from '../../../utils/helpers'; import { logWrapper } from '../../../utils/logWrapper'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; -import { sessionIdOption, sessionKeyProperty, contextWindowLengthProperty } from '../descriptions'; -import { getSessionId } from '../../../utils/helpers'; +import { + sessionIdOption, + sessionKeyProperty, + contextWindowLengthProperty, + expressionSessionKeyProperty, +} from '../descriptions'; export class MemoryRedisChat implements INodeType { description: INodeTypeDescription = { @@ -23,7 +29,7 @@ export class MemoryRedisChat implements INodeType { name: 'memoryRedisChat', icon: 'file:redis.svg', group: ['transform'], - version: [1, 1.1, 1.2, 1.3], + version: [1, 1.1, 1.2, 1.3, 1.4], description: 'Stores the chat history in Redis.', defaults: { name: 'Redis Chat Memory', @@ -86,6 +92,7 @@ export class MemoryRedisChat implements INodeType { }, }, }, + expressionSessionKeyProperty(1.4), sessionKeyProperty, { displayName: 'Session Time To Live', diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryXata/MemoryXata.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryXata/MemoryXata.node.ts index be431b9b3c55f..c48f32976ba6a 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryXata/MemoryXata.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryXata/MemoryXata.node.ts @@ -1,4 +1,7 @@ /* eslint-disable n8n-nodes-base/node-dirname-against-convention */ +import { XataChatMessageHistory } from '@langchain/community/stores/message/xata'; +import { BaseClient } from '@xata.io/client'; +import { BufferMemory, BufferWindowMemory } from 'langchain/memory'; import { NodeConnectionType, NodeOperationError } from 'n8n-workflow'; import type { ISupplyDataFunctions, @@ -6,13 +9,16 @@ import type { INodeTypeDescription, SupplyData, } from 'n8n-workflow'; -import { XataChatMessageHistory } from '@langchain/community/stores/message/xata'; -import { BufferMemory, BufferWindowMemory } from 'langchain/memory'; -import { BaseClient } from '@xata.io/client'; + +import { getSessionId } from '../../../utils/helpers'; import { logWrapper } from '../../../utils/logWrapper'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; -import { sessionIdOption, sessionKeyProperty, contextWindowLengthProperty } from '../descriptions'; -import { getSessionId } from '../../../utils/helpers'; +import { + sessionIdOption, + sessionKeyProperty, + contextWindowLengthProperty, + expressionSessionKeyProperty, +} from '../descriptions'; export class MemoryXata implements INodeType { description: INodeTypeDescription = { @@ -20,7 +26,7 @@ export class MemoryXata implements INodeType { name: 'memoryXata', icon: 'file:xata.svg', group: ['transform'], - version: [1, 1.1, 1.2, 1.3], + version: [1, 1.1, 1.2, 1.3, 1.4], description: 'Use Xata Memory', defaults: { name: 'Xata', @@ -86,6 +92,7 @@ export class MemoryXata implements INodeType { }, }, sessionKeyProperty, + expressionSessionKeyProperty(1.4), { ...contextWindowLengthProperty, displayOptions: { hide: { '@version': [{ _cnd: { lt: 1.3 } }] } }, diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryZep/MemoryZep.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryZep/MemoryZep.node.ts index 20e70fd92036c..42ae7244b42d7 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryZep/MemoryZep.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryZep/MemoryZep.node.ts @@ -12,7 +12,7 @@ import { ZepCloudMemory } from '@langchain/community/memory/zep_cloud'; import { logWrapper } from '../../../utils/logWrapper'; import { getConnectionHintNoticeField } from '../../../utils/sharedFields'; -import { sessionIdOption, sessionKeyProperty } from '../descriptions'; +import { expressionSessionKeyProperty, sessionIdOption, sessionKeyProperty } from '../descriptions'; import { getSessionId } from '../../../utils/helpers'; import type { BaseChatMemory } from '@langchain/community/dist/memory/chat_memory'; import type { InputValues, MemoryVariables } from '@langchain/core/memory'; @@ -36,7 +36,7 @@ export class MemoryZep implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg icon: 'file:zep.png', group: ['transform'], - version: [1, 1.1, 1.2], + version: [1, 1.1, 1.2, 1.3], description: 'Use Zep Memory', defaults: { name: 'Zep', @@ -99,6 +99,7 @@ export class MemoryZep implements INodeType { }, }, }, + expressionSessionKeyProperty(1.3), sessionKeyProperty, ], }; diff --git a/packages/@n8n/nodes-langchain/nodes/memory/descriptions.ts b/packages/@n8n/nodes-langchain/nodes/memory/descriptions.ts index 354d134fb7c42..4627671a9b5ae 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/descriptions.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/descriptions.ts @@ -21,6 +21,20 @@ export const sessionIdOption: INodeProperties = { default: 'fromInput', }; +export const expressionSessionKeyProperty = (fromVersion: number): INodeProperties => ({ + displayName: 'Session Key From Previous Node', + name: 'sessionKey', + type: 'string', + default: '={{ $json.sessionId }}', + disabledOptions: { show: { sessionIdType: ['fromInput'] } }, + displayOptions: { + show: { + sessionIdType: ['fromInput'], + '@version': [{ _cnd: { gte: fromVersion } }], + }, + }, +}); + export const sessionKeyProperty: INodeProperties = { displayName: 'Key', name: 'sessionKey', 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 cf770e40576c5..9af94a2121cfb 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 @@ -18,6 +18,7 @@ import { } from 'n8n-workflow'; import { OpenAI as OpenAIClient } from 'openai'; +import { promptTypeOptions, textFromPreviousNode } from '../../../../../utils/descriptions'; import { getConnectedTools } from '../../../../../utils/helpers'; import { getTracingConfig } from '../../../../../utils/tracing'; import { formatToOpenAIAssistantTool } from '../../helpers/utils'; @@ -26,24 +27,18 @@ import { assistantRLC } from '../descriptions'; const properties: INodeProperties[] = [ assistantRLC, { - displayName: 'Prompt', + ...promptTypeOptions, name: 'prompt', - type: 'options', - options: [ - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'Take from previous node automatically', - value: 'auto', - description: 'Looks for an input field called chatInput', - }, - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'Define below', - value: 'define', - description: 'Use an expression to reference data in previous nodes or enter static text', + }, + { + ...textFromPreviousNode, + disabledOptions: { show: { prompt: ['auto'] } }, + displayOptions: { + show: { + prompt: ['auto'], + '@version': [{ _cnd: { gte: 1.7 } }], }, - ], - default: 'auto', + }, }, { displayName: 'Text', diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts index 20d7bd471ed51..8beabcf0a4c91 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts @@ -77,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, 1.6], + version: [1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7], subtitle: `={{(${prettifyOperation})($parameter.resource, $parameter.operation)}}`, description: 'Message an assistant or GPT, analyze images, generate audio, etc.', defaults: { diff --git a/packages/@n8n/nodes-langchain/utils/descriptions.ts b/packages/@n8n/nodes-langchain/utils/descriptions.ts index b779df1be4a12..4a22e368942c8 100644 --- a/packages/@n8n/nodes-langchain/utils/descriptions.ts +++ b/packages/@n8n/nodes-langchain/utils/descriptions.ts @@ -66,7 +66,7 @@ export const inputSchemaField: INodeProperties = { }; export const promptTypeOptions: INodeProperties = { - displayName: 'Prompt', + displayName: 'Prompt Source', name: 'promptType', type: 'options', options: [ @@ -97,3 +97,15 @@ export const textInput: INodeProperties = { rows: 2, }, }; + +export const textFromPreviousNode: INodeProperties = { + displayName: 'Text From Previous Node', + name: 'text', + type: 'string', + required: true, + default: '={{ $json.chatInput }}', + typeOptions: { + rows: 2, + }, + disabledOptions: { show: { promptType: ['auto'] } }, +}; diff --git a/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue b/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue index 5816dab1ccc98..70c1abc78cf01 100644 --- a/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue +++ b/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalInput.vue @@ -74,7 +74,7 @@ const { segments, readEditorValue, editor, hasFocus, focus } = useExpressionEdit editorRef: root, editorValue, extensions, - isReadOnly: props.isReadOnly, + isReadOnly: computed(() => props.isReadOnly), autocompleteTelemetry: { enabled: true, parameterPath: props.path }, }); @@ -110,7 +110,15 @@ defineExpose({ editor }); diff --git a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorInput.vue b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorInput.vue index 19a869b2e694d..643af5980c757 100644 --- a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorInput.vue +++ b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorInput.vue @@ -56,6 +56,7 @@ const extensions = computed(() => [ infoBoxTooltips(), ]); const editorValue = ref(removeExpressionPrefix(props.modelValue)); + const { editor: editorRef, segments, @@ -68,7 +69,7 @@ const { editorRef: root, editorValue, extensions, - isReadOnly: props.isReadOnly, + isReadOnly: computed(() => props.isReadOnly), autocompleteTelemetry: { enabled: true, parameterPath: props.path }, additionalData: props.additionalData, }); @@ -133,6 +134,17 @@ defineExpose({ padding-left: 0; } :deep(.cm-content) { + --disabled-fill: var(--color-background-medium); padding-left: var(--spacing-2xs); + + &[aria-readonly='true'] { + background-color: var(--disabled-fill, var(--color-background-light)); + border-color: var(--disabled-border, var(--border-color-base)); + color: var(--disabled-color, var(--color-text-base)); + cursor: not-allowed; + + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } } diff --git a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue index 634c8d340f201..80f148b8d46b1 100644 --- a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue +++ b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue @@ -16,12 +16,14 @@ interface InlineExpressionEditorOutputProps { editorState?: EditorState; selection?: SelectionRange; visible?: boolean; + isReadOnly?: boolean; } withDefaults(defineProps(), { visible: false, editorState: undefined, selection: undefined, + isReadOnly: false, unresolvedExpression: undefined, }); @@ -51,7 +53,7 @@ onBeforeUnmount(() => { > -
+
{ // Focus input field when changing from fixed value to expression watch(isModelValueExpression, async (isExpression, wasExpression) => { - if (isExpression && !wasExpression) { + if (!props.isReadOnly && isExpression && !wasExpression) { await nextTick(); inputField.value?.focus(); } @@ -1497,7 +1497,7 @@ onUpdated(async () => { :disabled="isReadOnly" @update:model-value="valueChanged" /> -
+
diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index b4c815dbf20eb..16b6c1321e1ff 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -1,5 +1,5 @@