import { flatten } from 'lodash' import { ChatMessage, OpenAI, OpenAIAgent } from 'llamaindex' import { getBaseClasses } from '../../../../src/utils' import { EvaluationRunTracerLlama } from '../../../../evaluation/EvaluationRunTracerLlama' import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams, IServerSideEventStreamer, IUsedTool } from '../../../../src/Interface' class OpenAIFunctionAgent_LlamaIndex_Agents implements INode { label: string name: string version: number description: string type: string icon: string category: string baseClasses: string[] tags: string[] inputs: INodeParams[] sessionId?: string constructor(fields?: { sessionId?: string }) { this.label = 'OpenAI Tool Agent' this.name = 'openAIToolAgentLlamaIndex' this.version = 2.0 this.type = 'OpenAIToolAgent' this.category = 'Agents' this.icon = 'function.svg' this.description = `Agent that uses OpenAI Function Calling to pick the tools and args to call using LlamaIndex` this.baseClasses = [this.type, ...getBaseClasses(OpenAIAgent)] this.tags = ['LlamaIndex'] this.inputs = [ { label: 'Tools', name: 'tools', type: 'Tool_LlamaIndex', list: true }, { label: 'Memory', name: 'memory', type: 'BaseChatMemory' }, { label: 'OpenAI/Azure Chat Model', name: 'model', type: 'BaseChatModel_LlamaIndex' }, { label: 'System Message', name: 'systemMessage', type: 'string', rows: 4, optional: true, additionalParams: true } ] this.sessionId = fields?.sessionId } async init(): Promise { return null } async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { const memory = nodeData.inputs?.memory as FlowiseMemory const model = nodeData.inputs?.model as OpenAI const systemMessage = nodeData.inputs?.systemMessage as string let tools = nodeData.inputs?.tools tools = flatten(tools) const shouldStreamResponse = options.shouldStreamResponse const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer const chatId = options.chatId const chatHistory = [] as ChatMessage[] if (systemMessage) { chatHistory.push({ content: systemMessage, role: 'system' }) } const msgs = (await memory.getChatMessages(this.sessionId, false)) as IMessage[] for (const message of msgs) { if (message.type === 'apiMessage') { chatHistory.push({ content: message.message, role: 'assistant' }) } else if (message.type === 'userMessage') { chatHistory.push({ content: message.message, role: 'user' }) } } const agent = new OpenAIAgent({ tools, llm: model, chatHistory: chatHistory, verbose: process.env.DEBUG === 'true' ? true : false }) // these are needed for evaluation runs await EvaluationRunTracerLlama.injectEvaluationMetadata(nodeData, options, agent) let text = '' let isStreamingStarted = false const usedTools: IUsedTool[] = [] if (shouldStreamResponse) { const stream = await agent.chat({ message: input, chatHistory, stream: true, verbose: process.env.DEBUG === 'true' ? true : false }) for await (const chunk of stream) { text += chunk.response.delta if (!isStreamingStarted) { isStreamingStarted = true if (sseStreamer) { sseStreamer.streamStartEvent(chatId, chunk.response.delta) } if (chunk.sources.length) { for (const sourceTool of chunk.sources) { usedTools.push({ tool: sourceTool.tool?.metadata.name ?? '', toolInput: sourceTool.input, toolOutput: sourceTool.output as any }) } if (sseStreamer) { sseStreamer.streamUsedToolsEvent(chatId, usedTools) } } } if (sseStreamer) { sseStreamer.streamTokenEvent(chatId, chunk.response.delta) } } } else { const response = await agent.chat({ message: input, chatHistory, verbose: process.env.DEBUG === 'true' ? true : false }) if (response.sources.length) { for (const sourceTool of response.sources) { usedTools.push({ tool: sourceTool.tool?.metadata.name ?? '', toolInput: sourceTool.input, toolOutput: sourceTool.output as any }) } } text = response.response.message.content as string } await memory.addChatMessages( [ { text: input, type: 'userMessage' }, { text: text, type: 'apiMessage' } ], this.sessionId ) return usedTools.length ? { text: text, usedTools } : text } } module.exports = { nodeClass: OpenAIFunctionAgent_LlamaIndex_Agents }