diff --git a/packages/components/nodes/agents/CSVAgent/CSVAgent.ts b/packages/components/nodes/agents/CSVAgent/CSVAgent.ts index 8fcae727a..e93b52d39 100644 --- a/packages/components/nodes/agents/CSVAgent/CSVAgent.ts +++ b/packages/components/nodes/agents/CSVAgent/CSVAgent.ts @@ -1,9 +1,10 @@ -import { INode, INodeData, INodeParams, PromptTemplate } from '../../../src/Interface' +import { ICommonObject, INode, INodeData, INodeParams, PromptTemplate } from '../../../src/Interface' import { AgentExecutor } from 'langchain/agents' import { getBaseClasses } from '../../../src/utils' import { LoadPyodide, finalSystemPrompt, systemPrompt } from './core' import { LLMChain } from 'langchain/chains' import { BaseLanguageModel } from 'langchain/base_language' +import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler' class CSV_Agents implements INode { label: string @@ -17,7 +18,7 @@ class CSV_Agents implements INode { constructor() { this.label = 'CSV Agent' - this.name = 'csvAgentLLM' + this.name = 'csvAgent' this.type = 'AgentExecutor' this.category = 'Agents' this.icon = 'csvagent.png' @@ -43,10 +44,13 @@ class CSV_Agents implements INode { return undefined } - async run(nodeData: INodeData, input: string): Promise { + async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { const csvFileBase64 = nodeData.inputs?.csvFile as string const model = nodeData.inputs?.model as BaseLanguageModel + const loggerHandler = new ConsoleCallbackHandler(options.logger) + const handler = new CustomChainHandler(options.socketIO, options.socketIOClientId) + let files: string[] = [] if (csvFileBase64.startsWith('[') && csvFileBase64.endsWith(']')) { @@ -67,7 +71,7 @@ class CSV_Agents implements INode { // First load the csv file and get the dataframe dictionary of column types // For example using titanic.csv: {'PassengerId': 'int64', 'Survived': 'int64', 'Pclass': 'int64', 'Name': 'object', 'Sex': 'object', 'Age': 'float64', 'SibSp': 'int64', 'Parch': 'int64', 'Ticket': 'object', 'Fare': 'float64', 'Cabin': 'object', 'Embarked': 'object'} - let executionResult = '' + let dataframeColDict = '' try { const code = `import pandas as pd import base64 @@ -84,29 +88,29 @@ df = pd.read_csv(csv_data) my_dict = df.dtypes.astype(str).to_dict() print(my_dict) json.dumps(my_dict)` - executionResult = await pyodide.runPythonAsync(code) + dataframeColDict = await pyodide.runPythonAsync(code) } catch (error) { throw new Error(error) } - console.log('executionResult= ', executionResult) + options.logger.debug('[components/CSVAgent] [1] DataframeColDict =>', dataframeColDict) // Then tell GPT to come out with ONLY python code // For example: len(df), df[df['SibSp'] > 3]['PassengerId'].count() let pythonCode = '' - if (executionResult) { + if (dataframeColDict) { const chain = new LLMChain({ llm: model, prompt: PromptTemplate.fromTemplate(systemPrompt), verbose: process.env.DEBUG === 'true' ? true : false }) const inputs = { - dict: executionResult, + dict: dataframeColDict, question: input } - const res = await chain.call(inputs) + const res = await chain.call(inputs, [loggerHandler]) pythonCode = res?.text } - console.log('pythonCode= ', pythonCode) + options.logger.debug('[components/CSVAgent] [2] Generated Python Code =>', pythonCode) // Then run the code using Pyodide let finalResult = '' @@ -115,10 +119,10 @@ json.dumps(my_dict)` const code = `import pandas as pd\n${pythonCode}` finalResult = await pyodide.runPythonAsync(code) } catch (error) { - throw new Error(error) + throw new Error(pythonCode) } } - console.log('finalResult= ', finalResult) + options.logger.debug('[components/CSVAgent] [3] Pyodide Result =>', finalResult) // Finally, return a complete answer if (finalResult) { @@ -131,11 +135,19 @@ json.dumps(my_dict)` question: input, answer: finalResult } - const res = await chain.call(inputs) - return res?.text + + if (options.socketIO && options.socketIOClientId) { + const result = await chain.call(inputs, [loggerHandler, handler]) + options.logger.debug('[components/CSVAgent] [4] Final Result =>', result?.text) + return result?.text + } else { + const result = await chain.call(inputs, [loggerHandler]) + options.logger.debug('[components/CSVAgent] [4] Final Result =>', result?.text) + return result?.text + } } - return executionResult + return pythonCode } } diff --git a/packages/components/nodes/agents/CSVAgent/core.ts b/packages/components/nodes/agents/CSVAgent/core.ts index 95a1f3d3b..1c193091a 100644 --- a/packages/components/nodes/agents/CSVAgent/core.ts +++ b/packages/components/nodes/agents/CSVAgent/core.ts @@ -31,5 +31,5 @@ I will ask question, and you will output the Python code using pandas dataframe Question: {question} Output Code:` -export const finalSystemPrompt = `You are given the question: {question}. You have an answer to the question: {answer}. Rephrase the answer with more details. -Helpful Answer:` +export const finalSystemPrompt = `You are given the question: {question}. You have an answer to the question: {answer}. Rephrase the answer into a standalone answer. +Standalone Answer:` diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 203cee4fe..1da6a84ea 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -728,7 +728,7 @@ export const isFlowValidForStream = (reactFlowNodes: IReactFlowNode[], endingNod isValidChainOrAgent = !blacklistChains.includes(endingNodeData.name) } else if (endingNodeData.category === 'Agents') { // Agent that are available to stream - const whitelistAgents = ['openAIFunctionAgent'] + const whitelistAgents = ['openAIFunctionAgent', 'csvAgent'] isValidChainOrAgent = whitelistAgents.includes(endingNodeData.name) }