mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
146 lines
4.2 KiB
TypeScript
146 lines
4.2 KiB
TypeScript
import { HumanMessage } from '@langchain/core/messages';
|
|
import type { BaseMessagePromptTemplateLike } from '@langchain/core/prompts';
|
|
import {
|
|
AIMessagePromptTemplate,
|
|
PromptTemplate,
|
|
SystemMessagePromptTemplate,
|
|
HumanMessagePromptTemplate,
|
|
ChatPromptTemplate,
|
|
} from '@langchain/core/prompts';
|
|
import type { IExecuteFunctions } from 'n8n-workflow';
|
|
import { OperationalError } from 'n8n-workflow';
|
|
|
|
import { isChatInstance } from '@utils/helpers';
|
|
|
|
import { createImageMessage } from './imageUtils';
|
|
import type { MessageTemplate, PromptParams } from './types';
|
|
|
|
/**
|
|
* Creates a basic query template that may include format instructions
|
|
*/
|
|
function buildQueryTemplate(formatInstructions?: string): PromptTemplate {
|
|
return new PromptTemplate({
|
|
template: `{query}${formatInstructions ? '\n{formatInstructions}' : ''}`,
|
|
inputVariables: ['query'],
|
|
partialVariables: formatInstructions ? { formatInstructions } : undefined,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Process an array of message templates into LangChain message objects
|
|
*/
|
|
async function processMessageTemplates({
|
|
context,
|
|
itemIndex,
|
|
messages,
|
|
}: {
|
|
context: IExecuteFunctions;
|
|
itemIndex: number;
|
|
messages: MessageTemplate[];
|
|
}): Promise<BaseMessagePromptTemplateLike[]> {
|
|
return await Promise.all(
|
|
messages.map(async (message) => {
|
|
// Find the appropriate message class based on type
|
|
const messageClass = [
|
|
SystemMessagePromptTemplate,
|
|
AIMessagePromptTemplate,
|
|
HumanMessagePromptTemplate,
|
|
].find((m) => m.lc_name() === message.type);
|
|
|
|
if (!messageClass) {
|
|
throw new OperationalError('Invalid message type', {
|
|
extra: { messageType: message.type },
|
|
});
|
|
}
|
|
|
|
// Handle image messages specially for human messages
|
|
if (messageClass === HumanMessagePromptTemplate && message.messageType !== 'text') {
|
|
return await createImageMessage({ context, itemIndex, message });
|
|
}
|
|
|
|
// Process text messages
|
|
// Escape curly braces in the message to prevent LangChain from treating them as variables
|
|
return messageClass.fromTemplate(
|
|
(message.message || '').replace(/[{}]/g, (match) => match + match),
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Finalizes the prompt template by adding or updating the query in the message chain
|
|
*/
|
|
async function finalizePromptTemplate({
|
|
parsedMessages,
|
|
queryTemplate,
|
|
query,
|
|
}: {
|
|
parsedMessages: BaseMessagePromptTemplateLike[];
|
|
queryTemplate: PromptTemplate;
|
|
query?: string;
|
|
}): Promise<ChatPromptTemplate> {
|
|
// Check if the last message is a human message with multi-content array
|
|
const lastMessage = parsedMessages[parsedMessages.length - 1];
|
|
|
|
if (lastMessage instanceof HumanMessage && Array.isArray(lastMessage.content)) {
|
|
// Add the query to the existing human message content
|
|
const humanMessage = new HumanMessagePromptTemplate(queryTemplate);
|
|
|
|
// Format the message with the query and add the content synchronously
|
|
const formattedMessage = await humanMessage.format({ query });
|
|
|
|
// Create a new array with the existing content plus the new item
|
|
if (Array.isArray(lastMessage.content)) {
|
|
// Clone the current content array and add the new item
|
|
const updatedContent = [
|
|
...lastMessage.content,
|
|
{
|
|
text: formattedMessage.content.toString(),
|
|
type: 'text',
|
|
},
|
|
];
|
|
|
|
// Replace the content with the updated array
|
|
lastMessage.content = updatedContent;
|
|
}
|
|
} else {
|
|
// Otherwise, add a new human message with the query
|
|
parsedMessages.push(new HumanMessagePromptTemplate(queryTemplate));
|
|
}
|
|
|
|
return ChatPromptTemplate.fromMessages(parsedMessages);
|
|
}
|
|
|
|
/**
|
|
* Builds the appropriate prompt template based on model type (chat vs completion)
|
|
* and provided messages
|
|
*/
|
|
export async function createPromptTemplate({
|
|
context,
|
|
itemIndex,
|
|
llm,
|
|
messages,
|
|
formatInstructions,
|
|
query,
|
|
}: PromptParams) {
|
|
// Create base query template
|
|
const queryTemplate = buildQueryTemplate(formatInstructions);
|
|
|
|
// For non-chat models, just return the query template
|
|
if (!isChatInstance(llm)) {
|
|
return queryTemplate;
|
|
}
|
|
|
|
// For chat models, process the messages if provided
|
|
const parsedMessages = messages?.length
|
|
? await processMessageTemplates({ context, itemIndex, messages })
|
|
: [];
|
|
|
|
// Add or update the query in the message chain
|
|
return await finalizePromptTemplate({
|
|
parsedMessages,
|
|
queryTemplate,
|
|
query,
|
|
});
|
|
}
|