add separate logic for conversation chain for openai vision

This commit is contained in:
Henry 2024-03-09 17:01:22 +08:00
parent bce7ff9ada
commit c35eb0b7e5
2 changed files with 38 additions and 6 deletions

View File

@ -151,7 +151,6 @@ const prepareAgent = async (
if (llmSupportsVision(model)) { if (llmSupportsVision(model)) {
const visionChatModel = model as IVisionChatModal const visionChatModel = model as IVisionChatModal
// let humanImageMessages: HumanMessage[] = []
const messageContent = addImagesToMessages(nodeData, options, model.multiModalOption) const messageContent = addImagesToMessages(nodeData, options, model.multiModalOption)
if (messageContent?.length) { if (messageContent?.length) {

View File

@ -1,7 +1,16 @@
import { ConversationChain } from 'langchain/chains' import { ConversationChain } from 'langchain/chains'
import { ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate } from '@langchain/core/prompts' import {
ChatPromptTemplate,
HumanMessagePromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
BaseMessagePromptTemplateLike,
PromptTemplate
} from '@langchain/core/prompts'
import { RunnableSequence } from '@langchain/core/runnables' import { RunnableSequence } from '@langchain/core/runnables'
import { StringOutputParser } from '@langchain/core/output_parsers' import { StringOutputParser } from '@langchain/core/output_parsers'
import { BaseChatModel } from '@langchain/core/language_models/chat_models'
import { HumanMessage } from '@langchain/core/messages'
import { ConsoleCallbackHandler as LCConsoleCallbackHandler } from '@langchain/core/tracers/console' import { ConsoleCallbackHandler as LCConsoleCallbackHandler } from '@langchain/core/tracers/console'
import { checkInputs, Moderation, streamResponse } from '../../moderation/Moderation' import { checkInputs, Moderation, streamResponse } from '../../moderation/Moderation'
import { formatResponse } from '../../outputparsers/OutputParserHelpers' import { formatResponse } from '../../outputparsers/OutputParserHelpers'
@ -156,12 +165,29 @@ const prepareChatPrompt = (nodeData: INodeData, humanImageMessages: MessageConte
const memory = nodeData.inputs?.memory as FlowiseMemory const memory = nodeData.inputs?.memory as FlowiseMemory
const prompt = nodeData.inputs?.systemMessagePrompt as string const prompt = nodeData.inputs?.systemMessagePrompt as string
const chatPromptTemplate = nodeData.inputs?.chatPromptTemplate as ChatPromptTemplate const chatPromptTemplate = nodeData.inputs?.chatPromptTemplate as ChatPromptTemplate
let model = nodeData.inputs?.model as BaseChatModel
if (chatPromptTemplate && chatPromptTemplate.promptMessages.length) { if (chatPromptTemplate && chatPromptTemplate.promptMessages.length) {
const sysPrompt = chatPromptTemplate.promptMessages[0] const sysPrompt = chatPromptTemplate.promptMessages[0]
const humanPrompt = chatPromptTemplate.promptMessages[chatPromptTemplate.promptMessages.length - 1] const humanPrompt = chatPromptTemplate.promptMessages[chatPromptTemplate.promptMessages.length - 1]
const messages = [sysPrompt, new MessagesPlaceholder(memory.memoryKey ?? 'chat_history'), humanPrompt] const messages = [sysPrompt, new MessagesPlaceholder(memory.memoryKey ?? 'chat_history'), humanPrompt]
// OpenAI works better when separate images into standalone human messages
if (model instanceof ChatOpenAI && humanImageMessages.length) {
messages.push(new HumanMessage({ content: [...humanImageMessages] }))
} else if (humanImageMessages.length) {
const lastMessage = messages.pop() as HumanMessagePromptTemplate
const template = (lastMessage.prompt as PromptTemplate).template as string
const msg = HumanMessagePromptTemplate.fromTemplate([
...humanImageMessages,
{
text: template
}
])
msg.inputVariables = lastMessage.inputVariables
messages.push(msg)
}
const chatPrompt = ChatPromptTemplate.fromMessages(messages) const chatPrompt = ChatPromptTemplate.fromMessages(messages)
if ((chatPromptTemplate as any).promptValues) { if ((chatPromptTemplate as any).promptValues) {
// @ts-ignore // @ts-ignore
@ -171,12 +197,19 @@ const prepareChatPrompt = (nodeData: INodeData, humanImageMessages: MessageConte
return chatPrompt return chatPrompt
} }
const messages = [ const messages: BaseMessagePromptTemplateLike[] = [
SystemMessagePromptTemplate.fromTemplate(prompt ? prompt : systemMessage), SystemMessagePromptTemplate.fromTemplate(prompt ? prompt : systemMessage),
new MessagesPlaceholder(memory.memoryKey ?? 'chat_history'), new MessagesPlaceholder(memory.memoryKey ?? 'chat_history')
HumanMessagePromptTemplate.fromTemplate([`{${inputKey}}`, ...humanImageMessages])
] ]
// OpenAI works better when separate images into standalone human messages
if (model instanceof ChatOpenAI && humanImageMessages.length) {
messages.push(HumanMessagePromptTemplate.fromTemplate(`{${inputKey}}`))
messages.push(new HumanMessage({ content: [...humanImageMessages] }))
} else if (humanImageMessages.length) {
messages.push(HumanMessagePromptTemplate.fromTemplate([`{${inputKey}}`, ...humanImageMessages]))
}
const chatPrompt = ChatPromptTemplate.fromMessages(messages) const chatPrompt = ChatPromptTemplate.fromMessages(messages)
return chatPrompt return chatPrompt
@ -184,7 +217,7 @@ const prepareChatPrompt = (nodeData: INodeData, humanImageMessages: MessageConte
const prepareChain = (nodeData: INodeData, options: ICommonObject, sessionId?: string) => { const prepareChain = (nodeData: INodeData, options: ICommonObject, sessionId?: string) => {
const chatHistory = options.chatHistory const chatHistory = options.chatHistory
let model = nodeData.inputs?.model as ChatOpenAI let model = nodeData.inputs?.model as BaseChatModel
const memory = nodeData.inputs?.memory as FlowiseMemory const memory = nodeData.inputs?.memory as FlowiseMemory
const memoryKey = memory.memoryKey ?? 'chat_history' const memoryKey = memory.memoryKey ?? 'chat_history'