import { flatten } from 'lodash' import { Tool, StructuredTool } from '@langchain/core/tools' import { BaseChatModel } from '@langchain/core/language_models/chat_models' import { AIMessage, HumanMessage, SystemMessage } from '@langchain/core/messages' import { VectorStoreRetriever } from '@langchain/core/vectorstores' import { PromptTemplate } from '@langchain/core/prompts' import { AutoGPT } from 'langchain/experimental/autogpt' import { LLMChain } from 'langchain/chains' import { INode, INodeData, INodeParams } from '../../../src/Interface' import { checkInputs, Moderation } from '../../moderation/Moderation' import { formatResponse } from '../../outputparsers/OutputParserHelpers' type ObjectTool = StructuredTool const FINISH_NAME = 'finish' class AutoGPT_Agents implements INode { label: string name: string version: number description: string type: string icon: string category: string baseClasses: string[] inputs: INodeParams[] badge: string constructor() { this.label = 'AutoGPT' this.name = 'autoGPT' this.version = 2.0 this.type = 'AutoGPT' this.category = 'Agents' this.badge = 'DEPRECATING' this.icon = 'autogpt.svg' this.description = 'Autonomous agent with chain of thoughts for self-guided task completion' this.baseClasses = ['AutoGPT'] this.inputs = [ { label: 'Allowed Tools', name: 'tools', type: 'Tool', list: true }, { label: 'Chat Model', name: 'model', type: 'BaseChatModel' }, { label: 'Vector Store Retriever', name: 'vectorStoreRetriever', type: 'BaseRetriever' }, { label: 'AutoGPT Name', name: 'aiName', type: 'string', placeholder: 'Tom', optional: true }, { label: 'AutoGPT Role', name: 'aiRole', type: 'string', placeholder: 'Assistant', optional: true }, { label: 'Maximum Loop', name: 'maxLoop', type: 'number', default: 5, optional: true }, { label: 'Input Moderation', description: 'Detect text that could generate harmful output and prevent it from being sent to the language model', name: 'inputModeration', type: 'Moderation', optional: true, list: true } ] } async init(nodeData: INodeData): Promise { const model = nodeData.inputs?.model as BaseChatModel const vectorStoreRetriever = nodeData.inputs?.vectorStoreRetriever as VectorStoreRetriever let tools = nodeData.inputs?.tools as Tool[] tools = flatten(tools) const aiName = (nodeData.inputs?.aiName as string) || 'AutoGPT' const aiRole = (nodeData.inputs?.aiRole as string) || 'Assistant' const maxLoop = nodeData.inputs?.maxLoop as string const autogpt = AutoGPT.fromLLMAndTools(model, tools, { memory: vectorStoreRetriever, aiName, aiRole }) autogpt.maxIterations = parseInt(maxLoop, 10) return autogpt } async run(nodeData: INodeData, input: string): Promise { const executor = nodeData.instance as AutoGPT const model = nodeData.inputs?.model as BaseChatModel const moderations = nodeData.inputs?.inputModeration as Moderation[] if (moderations && moderations.length > 0) { try { // Use the output of the moderation chain as input for the AutoGPT agent input = await checkInputs(moderations, input) } catch (e) { await new Promise((resolve) => setTimeout(resolve, 500)) // if (options.shouldStreamResponse) { // streamResponse(options.sseStreamer, options.chatId, e.message) // } return formatResponse(e.message) } } try { let totalAssistantReply = '' executor.run = async (goals: string[]): Promise => { const user_input = 'Determine which next command to use, and respond using the format specified above:' let loopCount = 0 while (loopCount < executor.maxIterations) { loopCount += 1 const { text: assistantReply } = await executor.chain.call({ goals, user_input, memory: executor.memory, messages: executor.fullMessageHistory }) // eslint-disable-next-line no-console console.log('\x1b[92m\x1b[1m\n*****AutoGPT*****\n\x1b[0m\x1b[0m') // eslint-disable-next-line no-console console.log(assistantReply) totalAssistantReply += assistantReply + '\n' executor.fullMessageHistory.push(new HumanMessage(user_input)) executor.fullMessageHistory.push(new AIMessage(assistantReply)) const action = await executor.outputParser.parse(assistantReply) const tools = executor.tools.reduce((acc, tool) => ({ ...acc, [tool.name]: tool }), {} as { [key: string]: ObjectTool }) if (action.name === FINISH_NAME) { return action.args.response } let result: string if (action.name in tools) { const tool = tools[action.name] let observation try { observation = await tool.call(action.args) } catch (e) { observation = `Error in args: ${e}` } result = `Command ${tool.name} returned: ${observation}` } else if (action.name === 'ERROR') { result = `Error: ${action.args}. ` } else { result = `Unknown command '${action.name}'. Please refer to the 'COMMANDS' list for available commands and only respond in the specified JSON format.` } let memoryToAdd = `Assistant Reply: ${assistantReply}\nResult: ${result} ` if (executor.feedbackTool) { const feedback = `\n${await executor.feedbackTool.call('Input: ')}` if (feedback === 'q' || feedback === 'stop') { return 'EXITING' } memoryToAdd += feedback } const documents = await executor.textSplitter.createDocuments([memoryToAdd]) await executor.memory.addDocuments(documents) executor.fullMessageHistory.push(new SystemMessage(result)) } return undefined } const res = await executor.run([input]) if (!res) { const sentence = `Unfortunately I was not able to complete all the task. Here is the chain of thoughts:` return `${await rephraseString(sentence, model)}\n\`\`\`javascript\n${totalAssistantReply}\n\`\`\`\n` } const sentence = `I have completed all my tasks. Here is the chain of thoughts:` let writeFilePath = '' const writeTool = executor.tools.find((tool) => tool.name === 'write_file') if (executor.tools.length && writeTool) { writeFilePath = (writeTool as any).store.basePath } return `${await rephraseString( sentence, model )}\n\`\`\`javascript\n${totalAssistantReply}\n\`\`\`\nAnd the final result:\n\`\`\`javascript\n${res}\n\`\`\`\n${ writeFilePath ? await rephraseString( `You can download the final result displayed above, or see if a new file has been successfully written to \`${writeFilePath}\``, model ) : '' }` } catch (e) { throw new Error(e) } } } const rephraseString = async (sentence: string, model: BaseChatModel) => { const promptTemplate = new PromptTemplate({ template: 'You are a helpful Assistant that rephrase a sentence: {sentence}', inputVariables: ['sentence'] }) const chain = new LLMChain({ llm: model, prompt: promptTemplate }) const res = await chain.call({ sentence }) return res?.text } module.exports = { nodeClass: AutoGPT_Agents }