dtamind-components
Version:
Apps integration for Dtamind. Contain Nodes and Credentials.
895 lines (893 loc) • 38 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsonOutputToolsParser = exports.ToolCallingAgentOutputParser = exports.XMLAgentOutputParser = exports.createReactAgent = exports.formatAgentSteps = exports.AgentExecutor = exports.AgentExecutorIterator = exports.TOOL_ARGS_PREFIX = exports.ARTIFACTS_PREFIX = exports.SOURCE_DOCUMENTS_PREFIX = void 0;
const lodash_1 = require("lodash");
const messages_1 = require("@langchain/core/messages");
const output_parsers_1 = require("@langchain/core/output_parsers");
const manager_1 = require("@langchain/core/callbacks/manager");
const tools_1 = require("@langchain/core/tools");
const runnables_1 = require("@langchain/core/runnables");
const serializable_1 = require("@langchain/core/load/serializable");
const prompts_1 = require("@langchain/core/prompts");
const chains_1 = require("langchain/chains");
const agents_1 = require("langchain/agents");
const log_1 = require("langchain/agents/format_scratchpad/log");
const error_1 = require("./error");
exports.SOURCE_DOCUMENTS_PREFIX = '\n\n----FLOWISE_SOURCE_DOCUMENTS----\n\n';
exports.ARTIFACTS_PREFIX = '\n\n----FLOWISE_ARTIFACTS----\n\n';
exports.TOOL_ARGS_PREFIX = '\n\n----FLOWISE_TOOL_ARGS----\n\n';
//TODO: stream tools back
class AgentExecutorIterator extends serializable_1.Serializable {
get finalOutputs() {
return this._finalOutputs;
}
/** Intended to be used as a setter method, needs to be async. */
async setFinalOutputs(value) {
this._finalOutputs = undefined;
if (value) {
const preparedOutputs = await this.agentExecutor.prepOutputs(this.inputs, value, true);
this._finalOutputs = preparedOutputs;
}
}
get nameToToolMap() {
const toolMap = this.agentExecutor.tools.map((tool) => ({
[tool.name]: tool
}));
return Object.assign({}, ...toolMap);
}
constructor(fields) {
super(fields);
this.lc_namespace = ['langchain', 'agents', 'executor_iterator'];
this.intermediateSteps = [];
this.iterations = 0;
this.agentExecutor = fields.agentExecutor;
this.inputs = fields.inputs;
this.tags = fields.tags;
this.metadata = fields.metadata;
this.runName = fields.runName;
this.runManager = fields.runManager;
this.config = fields.config;
}
/**
* Reset the iterator to its initial state, clearing intermediate steps,
* iterations, and the final output.
*/
reset() {
this.intermediateSteps = [];
this.iterations = 0;
this._finalOutputs = undefined;
}
updateIterations() {
this.iterations += 1;
}
async *streamIterator() {
this.reset();
// Loop to handle iteration
while (true) {
try {
if (this.iterations === 0) {
await this.onFirstStep();
}
const result = await this._callNext();
yield result;
}
catch (e) {
if ('message' in e && e.message.startsWith('Final outputs already reached: ')) {
if (!this.finalOutputs) {
throw e;
}
return this.finalOutputs;
}
if (this.runManager) {
await this.runManager.handleChainError(e);
}
throw e;
}
}
}
/**
* Perform any necessary setup for the first step
* of the asynchronous iterator.
*/
async onFirstStep() {
if (this.iterations === 0) {
const callbackManager = await manager_1.CallbackManager.configure(this.callbacks, this.agentExecutor.callbacks, this.tags, this.agentExecutor.tags, this.metadata, this.agentExecutor.metadata, {
verbose: this.agentExecutor.verbose
});
this.runManager = await callbackManager?.handleChainStart(this.agentExecutor.toJSON(), this.inputs, undefined, undefined, this.tags, this.metadata, this.runName);
}
}
/**
* Execute the next step in the chain using the
* AgentExecutor's _takeNextStep method.
*/
async _executeNextStep(runManager) {
return this.agentExecutor._takeNextStep(this.nameToToolMap, this.inputs, this.intermediateSteps, runManager);
}
/**
* Process the output of the next step,
* handling AgentFinish and tool return cases.
*/
async _processNextStepOutput(nextStepOutput, runManager) {
if ('returnValues' in nextStepOutput) {
const output = await this.agentExecutor._return(nextStepOutput, this.intermediateSteps, runManager);
if (this.runManager) {
await this.runManager.handleChainEnd(output);
}
await this.setFinalOutputs(output);
return output;
}
this.intermediateSteps = this.intermediateSteps.concat(nextStepOutput);
let output = {};
if (Array.isArray(nextStepOutput) && nextStepOutput.length === 1) {
const nextStep = nextStepOutput[0];
const toolReturn = await this.agentExecutor._getToolReturn(nextStep);
if (toolReturn) {
output = await this.agentExecutor._return(toolReturn, this.intermediateSteps, runManager);
if (this.runManager) {
await this.runManager.handleChainEnd(output);
}
await this.setFinalOutputs(output);
}
}
output = { intermediateSteps: nextStepOutput };
return output;
}
async _stop() {
const output = await this.agentExecutor.agent.returnStoppedResponse(this.agentExecutor.earlyStoppingMethod, this.intermediateSteps, this.inputs);
const returnedOutput = await this.agentExecutor._return(output, this.intermediateSteps, this.runManager);
await this.setFinalOutputs(returnedOutput);
return returnedOutput;
}
async _callNext() {
// final output already reached: stopiteration (final output)
if (this.finalOutputs) {
throw new Error(`Final outputs already reached: ${JSON.stringify(this.finalOutputs, null, 2)}`);
}
// timeout/max iterations: stopiteration (stopped response)
if (!this.agentExecutor.shouldContinueGetter(this.iterations)) {
return this._stop();
}
const nextStepOutput = await this._executeNextStep(this.runManager);
const output = await this._processNextStepOutput(nextStepOutput, this.runManager);
this.updateIterations();
return output;
}
}
exports.AgentExecutorIterator = AgentExecutorIterator;
class AgentExecutor extends chains_1.BaseChain {
static lc_name() {
return 'AgentExecutor';
}
get lc_namespace() {
return ['langchain', 'agents', 'executor'];
}
get inputKeys() {
return this.agent.inputKeys;
}
get outputKeys() {
return this.agent.returnValues;
}
constructor(input) {
let agent;
if (runnables_1.Runnable.isRunnable(input.agent)) {
agent = new agents_1.RunnableAgent({ runnable: input.agent });
}
else {
agent = input.agent;
}
super(input);
this.returnIntermediateSteps = false;
this.maxIterations = 15;
this.earlyStoppingMethod = 'force';
/**
* How to handle errors raised by the agent's output parser.
Defaults to `False`, which raises the error.
If `true`, the error will be sent back to the LLM as an observation.
If a string, the string itself will be sent to the LLM as an observation.
If a callable function, the function will be called with the exception
as an argument, and the result of that function will be passed to the agent
as an observation.
*/
this.handleParsingErrors = false;
this.agent = agent;
this.tools = input.tools;
this.handleParsingErrors = input.handleParsingErrors ?? this.handleParsingErrors;
/* Getting rid of this because RunnableAgent doesnt allow return direct
if (this.agent._agentActionType() === "multi") {
for (const tool of this.tools) {
if (tool.returnDirect) {
throw new Error(
`Tool with return direct ${tool.name} not supported for multi-action agent.`
);
}
}
}*/
this.returnIntermediateSteps = input.returnIntermediateSteps ?? this.returnIntermediateSteps;
this.maxIterations = input.maxIterations ?? this.maxIterations;
this.earlyStoppingMethod = input.earlyStoppingMethod ?? this.earlyStoppingMethod;
this.sessionId = input.sessionId;
this.chatId = input.chatId;
this.input = input.input;
this.isXML = input.isXML;
}
static fromAgentAndTools(fields) {
const newInstance = new AgentExecutor(fields);
if (fields.sessionId)
newInstance.sessionId = fields.sessionId;
if (fields.chatId)
newInstance.chatId = fields.chatId;
if (fields.input)
newInstance.input = fields.input;
if (fields.isXML)
newInstance.isXML = fields.isXML;
return newInstance;
}
get shouldContinueGetter() {
return this.shouldContinue.bind(this);
}
/**
* Method that checks if the agent execution should continue based on the
* number of iterations.
* @param iterations The current number of iterations.
* @returns A boolean indicating whether the agent execution should continue.
*/
shouldContinue(iterations) {
return this.maxIterations === undefined || iterations < this.maxIterations;
}
async _call(inputs, runManager, config) {
const toolsByName = Object.fromEntries(this.tools.map((t) => [t.name?.toLowerCase(), t]));
const steps = [];
let iterations = 0;
let sourceDocuments = [];
const usedTools = [];
let artifacts = [];
const getOutput = async (finishStep) => {
const { returnValues } = finishStep;
const additional = await this.agent.prepareForOutput(returnValues, steps);
if (sourceDocuments.length)
additional.sourceDocuments = (0, lodash_1.flatten)(sourceDocuments);
if (usedTools.length)
additional.usedTools = usedTools;
if (artifacts.length)
additional.artifacts = (0, lodash_1.flatten)(artifacts);
if (this.returnIntermediateSteps) {
return { ...returnValues, intermediateSteps: steps, ...additional };
}
await runManager?.handleAgentEnd(finishStep);
return { ...returnValues, ...additional };
};
while (this.shouldContinue(iterations)) {
let output;
try {
output = await this.agent.plan(steps, inputs, runManager?.getChild(), config);
}
catch (e) {
if (e instanceof output_parsers_1.OutputParserException) {
let observation;
let text = e.message;
if (this.handleParsingErrors === true) {
if (e.sendToLLM) {
observation = e.observation;
text = e.llmOutput ?? '';
}
else {
observation = 'Invalid or incomplete response';
}
}
else if (typeof this.handleParsingErrors === 'string') {
observation = this.handleParsingErrors;
}
else if (typeof this.handleParsingErrors === 'function') {
observation = this.handleParsingErrors(e);
}
else {
throw e;
}
output = {
tool: '_Exception',
toolInput: observation,
log: text
};
}
else {
throw e;
}
}
// Check if the agent has finished
if ('returnValues' in output) {
return getOutput(output);
}
let actions;
if (Array.isArray(output)) {
actions = output;
}
else {
actions = [output];
}
const newSteps = await Promise.all(actions.map(async (action) => {
await runManager?.handleAgentAction(action);
const tool = action.tool === '_Exception' ? new ExceptionTool() : toolsByName[action.tool?.toLowerCase()];
let observation;
try {
/* Here we need to override Tool call method to include sessionId, chatId, input as parameter
* Tool Call Parameters:
* - arg: z.output<T>
* - configArg?: RunnableConfig | Callbacks
* - tags?: string[]
* - flowConfig?: { sessionId?: string, chatId?: string, input?: string }
*/
if (tool) {
observation = await tool.call(this.isXML && typeof action.toolInput === 'string' ? { input: action.toolInput } : action.toolInput, runManager?.getChild(), undefined, {
sessionId: this.sessionId,
chatId: this.chatId,
input: this.input,
state: inputs
});
let toolOutput = observation;
if (typeof toolOutput === 'string' && toolOutput.includes(exports.SOURCE_DOCUMENTS_PREFIX)) {
toolOutput = toolOutput.split(exports.SOURCE_DOCUMENTS_PREFIX)[0];
}
if (typeof toolOutput === 'string' && toolOutput.includes(exports.ARTIFACTS_PREFIX)) {
toolOutput = toolOutput.split(exports.ARTIFACTS_PREFIX)[0];
}
let toolInput;
if (typeof toolOutput === 'string' && toolOutput.includes(exports.TOOL_ARGS_PREFIX)) {
const splitArray = toolOutput.split(exports.TOOL_ARGS_PREFIX);
toolOutput = splitArray[0];
try {
toolInput = JSON.parse(splitArray[1]);
}
catch (e) {
console.error('Error parsing tool input from tool');
}
}
usedTools.push({
tool: tool.name,
toolInput: toolInput ?? action.toolInput,
toolOutput
});
}
else {
observation = `${action.tool} is not a valid tool, try another one.`;
}
}
catch (e) {
if (e instanceof tools_1.ToolInputParsingException) {
if (this.handleParsingErrors === true) {
observation = 'Invalid or incomplete tool input. Please try again.';
}
else if (typeof this.handleParsingErrors === 'string') {
observation = this.handleParsingErrors;
}
else if (typeof this.handleParsingErrors === 'function') {
observation = this.handleParsingErrors(e);
}
else {
throw e;
}
observation = await new ExceptionTool().call(observation, runManager?.getChild());
usedTools.push({
tool: tool.name,
toolInput: action.toolInput,
toolOutput: '',
error: (0, error_1.getErrorMessage)(e)
});
return { action, observation: observation ?? '' };
}
else {
usedTools.push({
tool: tool.name,
toolInput: action.toolInput,
toolOutput: '',
error: (0, error_1.getErrorMessage)(e)
});
return { action, observation: (0, error_1.getErrorMessage)(e) };
}
}
if (typeof observation === 'string' && observation.includes(exports.SOURCE_DOCUMENTS_PREFIX)) {
const observationArray = observation.split(exports.SOURCE_DOCUMENTS_PREFIX);
observation = observationArray[0];
const docs = observationArray[1];
try {
const parsedDocs = JSON.parse(docs);
sourceDocuments.push(parsedDocs);
}
catch (e) {
console.error('Error parsing source documents from tool');
}
}
if (typeof observation === 'string' && observation.includes(exports.ARTIFACTS_PREFIX)) {
const observationArray = observation.split(exports.ARTIFACTS_PREFIX);
observation = observationArray[0];
try {
const artifact = JSON.parse(observationArray[1]);
artifacts.push(artifact);
}
catch (e) {
console.error('Error parsing source documents from tool');
}
}
if (typeof observation === 'string' && observation.includes(exports.TOOL_ARGS_PREFIX)) {
const observationArray = observation.split(exports.TOOL_ARGS_PREFIX);
observation = observationArray[0];
}
return { action, observation: observation ?? '' };
}));
steps.push(...newSteps);
const lastStep = steps[steps.length - 1];
const lastTool = toolsByName[lastStep.action.tool?.toLowerCase()];
if (lastTool?.returnDirect) {
return getOutput({
returnValues: { [this.agent.returnValues[0]]: lastStep.observation },
log: ''
});
}
iterations += 1;
}
const finish = await this.agent.returnStoppedResponse(this.earlyStoppingMethod, steps, inputs);
return getOutput(finish);
}
async _takeNextStep(nameToolMap, inputs, intermediateSteps, runManager, config) {
let output;
try {
output = await this.agent.plan(intermediateSteps, inputs, runManager?.getChild(), config);
}
catch (e) {
if (e instanceof output_parsers_1.OutputParserException) {
let observation;
let text = e.message;
if (this.handleParsingErrors === true) {
if (e.sendToLLM) {
observation = e.observation;
text = e.llmOutput ?? '';
}
else {
observation = 'Invalid or incomplete response';
}
}
else if (typeof this.handleParsingErrors === 'string') {
observation = this.handleParsingErrors;
}
else if (typeof this.handleParsingErrors === 'function') {
observation = this.handleParsingErrors(e);
}
else {
throw e;
}
output = {
tool: '_Exception',
toolInput: observation,
log: text
};
}
else {
throw e;
}
}
if ('returnValues' in output) {
return output;
}
let actions;
if (Array.isArray(output)) {
actions = output;
}
else {
actions = [output];
}
const result = [];
for (const agentAction of actions) {
let observation = '';
if (runManager) {
await runManager?.handleAgentAction(agentAction);
}
if (agentAction.tool in nameToolMap) {
const tool = nameToolMap[agentAction.tool];
try {
/* Here we need to override Tool call method to include sessionId, chatId, input as parameter
* Tool Call Parameters:
* - arg: z.output<T>
* - configArg?: RunnableConfig | Callbacks
* - tags?: string[]
* - flowConfig?: { sessionId?: string, chatId?: string, input?: string }
*/
observation = await tool.call(this.isXML && typeof agentAction.toolInput === 'string' ? { input: agentAction.toolInput } : agentAction.toolInput, runManager?.getChild(), undefined, {
sessionId: this.sessionId,
chatId: this.chatId,
input: this.input,
state: inputs
});
if (typeof observation === 'string' && observation.includes(exports.SOURCE_DOCUMENTS_PREFIX)) {
const observationArray = observation.split(exports.SOURCE_DOCUMENTS_PREFIX);
observation = observationArray[0];
}
if (typeof observation === 'string' && observation.includes(exports.ARTIFACTS_PREFIX)) {
const observationArray = observation.split(exports.ARTIFACTS_PREFIX);
observation = observationArray[0];
}
if (typeof observation === 'string' && observation.includes(exports.TOOL_ARGS_PREFIX)) {
const observationArray = observation.split(exports.TOOL_ARGS_PREFIX);
observation = observationArray[0];
}
}
catch (e) {
if (e instanceof tools_1.ToolInputParsingException) {
if (this.handleParsingErrors === true) {
observation = 'Invalid or incomplete tool input. Please try again.';
}
else if (typeof this.handleParsingErrors === 'string') {
observation = this.handleParsingErrors;
}
else if (typeof this.handleParsingErrors === 'function') {
observation = this.handleParsingErrors(e);
}
else {
throw e;
}
observation = await new ExceptionTool().call(observation, runManager?.getChild());
}
}
}
else {
observation = `${agentAction.tool} is not a valid tool, try another available tool: ${Object.keys(nameToolMap).join(', ')}`;
}
result.push({
action: agentAction,
observation
});
}
return result;
}
async _return(output, intermediateSteps, runManager) {
if (runManager) {
await runManager.handleAgentEnd(output);
}
const finalOutput = output.returnValues;
if (this.returnIntermediateSteps) {
finalOutput.intermediateSteps = intermediateSteps;
}
return finalOutput;
}
async _getToolReturn(nextStepOutput) {
const { action, observation } = nextStepOutput;
const nameToolMap = Object.fromEntries(this.tools.map((t) => [t.name?.toLowerCase(), t]));
const [returnValueKey = 'output'] = this.agent.returnValues;
// Invalid tools won't be in the map, so we return False.
if (action.tool in nameToolMap) {
if (nameToolMap[action.tool].returnDirect) {
return {
returnValues: { [returnValueKey]: observation },
log: ''
};
}
}
return null;
}
_returnStoppedResponse(earlyStoppingMethod) {
if (earlyStoppingMethod === 'force') {
return {
returnValues: {
output: 'Agent stopped due to iteration limit or time limit.'
},
log: ''
};
}
throw new Error(`Got unsupported early_stopping_method: ${earlyStoppingMethod}`);
}
async *_streamIterator(inputs, options) {
const agentExecutorIterator = new AgentExecutorIterator({
inputs,
agentExecutor: this,
config: options,
metadata: this.metadata,
tags: this.tags,
callbacks: this.callbacks
});
const iterator = agentExecutorIterator.streamIterator();
for await (const step of iterator) {
if (!step) {
continue;
}
yield step;
}
}
_chainType() {
return 'agent_executor';
}
serialize() {
throw new Error('Cannot serialize an AgentExecutor');
}
}
exports.AgentExecutor = AgentExecutor;
class ExceptionTool extends tools_1.Tool {
constructor() {
super(...arguments);
this.name = '_Exception';
this.description = 'Exception tool';
}
async _call(query) {
return query;
}
}
const formatAgentSteps = (steps) => steps.flatMap(({ action, observation }) => {
const create_function_message = (observation, action) => {
let content;
if (typeof observation !== 'string') {
content = JSON.stringify(observation);
}
else {
content = observation;
}
return new messages_1.FunctionMessage(content, action.tool);
};
if ('messageLog' in action && action.messageLog !== undefined) {
const log = action.messageLog;
return log.concat(create_function_message(observation, action));
}
else {
return [new messages_1.AIMessage(action.log)];
}
});
exports.formatAgentSteps = formatAgentSteps;
const renderTextDescription = (tools) => {
return tools.map((tool) => `${tool.name}: ${tool.description}`).join('\n');
};
const createReactAgent = async ({ llm, tools, prompt }) => {
const missingVariables = ['tools', 'tool_names', 'agent_scratchpad'].filter((v) => !prompt.inputVariables.includes(v));
if (missingVariables.length > 0) {
throw new Error(`Provided prompt is missing required input variables: ${JSON.stringify(missingVariables)}`);
}
const toolNames = tools.map((tool) => tool.name);
const partialedPrompt = await prompt.partial({
tools: renderTextDescription(tools),
tool_names: toolNames.join(', ')
});
// TODO: Add .bind to core runnable interface.
const llmWithStop = llm.bind({
stop: ['\nObservation:']
});
const agent = runnables_1.RunnableSequence.from([
runnables_1.RunnablePassthrough.assign({
//@ts-ignore
agent_scratchpad: (input) => (0, log_1.formatLogToString)(input.steps)
}),
partialedPrompt,
llmWithStop,
new ReActSingleInputOutputParser({
toolNames
})
]);
return agent;
};
exports.createReactAgent = createReactAgent;
class ReActSingleInputOutputParser extends agents_1.AgentActionOutputParser {
constructor(fields) {
super(...arguments);
this.lc_namespace = ['langchain', 'agents', 'react'];
this.FINAL_ANSWER_ACTION = 'Final Answer:';
this.FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = 'Parsing LLM output produced both a final answer and a parse-able action:';
this.FORMAT_INSTRUCTIONS = `Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question`;
this.toolNames = fields.toolNames;
}
/**
* Parses the given text into an AgentAction or AgentFinish object. If an
* output fixing parser is defined, uses it to parse the text.
* @param text Text to parse.
* @returns Promise that resolves to an AgentAction or AgentFinish object.
*/
async parse(text) {
const includesAnswer = text.includes(this.FINAL_ANSWER_ACTION);
const regex = /Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)/;
const actionMatch = text.match(regex);
if (actionMatch) {
if (includesAnswer) {
throw new Error(`${this.FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}: ${text}`);
}
const action = actionMatch[1];
const actionInput = actionMatch[2];
const toolInput = actionInput.trim().replace(/"/g, '');
return {
tool: action,
toolInput,
log: text
};
}
if (includesAnswer) {
const finalAnswerText = text.split(this.FINAL_ANSWER_ACTION)[1].trim();
return {
returnValues: {
output: finalAnswerText
},
log: text
};
}
// Instead of throwing Error, we return a AgentFinish object
return { returnValues: { output: text }, log: text };
}
/**
* Returns the format instructions as a string. If the 'raw' option is
* true, returns the raw FORMAT_INSTRUCTIONS.
* @param options Options for getting the format instructions.
* @returns Format instructions as a string.
*/
getFormatInstructions() {
return (0, prompts_1.renderTemplate)(this.FORMAT_INSTRUCTIONS, 'f-string', {
tool_names: this.toolNames.join(', ')
});
}
}
class XMLAgentOutputParser extends agents_1.AgentActionOutputParser {
constructor() {
super(...arguments);
this.lc_namespace = ['langchain', 'agents', 'xml'];
}
static lc_name() {
return 'XMLAgentOutputParser';
}
/**
* Parses the output text from the agent and returns an AgentAction or
* AgentFinish object.
* @param text The output text from the agent.
* @returns An AgentAction or AgentFinish object.
*/
async parse(text) {
if (text.includes('</tool>')) {
const [tool, toolInput] = text.split('</tool>');
const _tool = tool.split('<tool>')[1];
const _toolInput = toolInput.split('<tool_input>')[1];
return { tool: _tool, toolInput: _toolInput, log: text };
}
else if (text.includes('<final_answer>')) {
const [, answer] = text.split('<final_answer>');
return { returnValues: { output: answer }, log: text };
}
else {
// Instead of throwing Error, we return a AgentFinish object
return { returnValues: { output: text }, log: text };
}
}
getFormatInstructions() {
throw new Error('getFormatInstructions not implemented inside XMLAgentOutputParser.');
}
}
exports.XMLAgentOutputParser = XMLAgentOutputParser;
class AgentMultiActionOutputParser extends output_parsers_1.BaseOutputParser {
}
function parseAIMessageToToolAction(message) {
const stringifiedMessageContent = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
let toolCalls = [];
if (message.tool_calls !== undefined && message.tool_calls.length > 0) {
toolCalls = message.tool_calls;
}
else {
if (message.additional_kwargs.tool_calls === undefined || message.additional_kwargs.tool_calls.length === 0) {
return {
returnValues: { output: message.content },
log: stringifiedMessageContent
};
}
// Best effort parsing
for (const toolCall of message.additional_kwargs.tool_calls ?? []) {
const functionName = toolCall.function?.name;
try {
const args = JSON.parse(toolCall.function.arguments);
toolCalls.push({ name: functionName, args, id: toolCall.id });
}
catch (e) {
throw new output_parsers_1.OutputParserException(`Failed to parse tool arguments from chat model response. Text: "${JSON.stringify(toolCalls)}". ${e}`);
}
}
}
return toolCalls.map((toolCall, i) => {
const messageLog = i === 0 ? [message] : [];
const log = `Invoking "${toolCall.name}" with ${JSON.stringify(toolCall.args ?? {})}\n${stringifiedMessageContent}`;
return {
tool: toolCall.name,
toolInput: toolCall.args,
toolCallId: toolCall.id ?? '',
log,
messageLog
};
});
}
class ToolCallingAgentOutputParser extends AgentMultiActionOutputParser {
constructor() {
super(...arguments);
this.lc_namespace = ['langchain', 'agents', 'tool_calling'];
}
static lc_name() {
return 'ToolCallingAgentOutputParser';
}
async parse(text) {
throw new Error(`ToolCallingAgentOutputParser can only parse messages.\nPassed input: ${text}`);
}
async parseResult(generations) {
if ('message' in generations[0] && (0, messages_1.isBaseMessage)(generations[0].message)) {
return parseAIMessageToToolAction(generations[0].message);
}
throw new Error('parseResult on ToolCallingAgentOutputParser only works on ChatGeneration output');
}
getFormatInstructions() {
throw new Error('getFormatInstructions not implemented inside ToolCallingAgentOutputParser.');
}
}
exports.ToolCallingAgentOutputParser = ToolCallingAgentOutputParser;
class JsonOutputToolsParser extends output_parsers_1.BaseLLMOutputParser {
static lc_name() {
return 'JsonOutputToolsParser';
}
constructor(fields) {
super(fields);
this.returnId = false;
this.lc_namespace = ['langchain', 'output_parsers', 'openai_tools'];
this.lc_serializable = true;
this.returnId = fields?.returnId ?? this.returnId;
}
/**
* Parses the output and returns a JSON object. If `argsOnly` is true,
* only the arguments of the function call are returned.
* @param generations The output of the LLM to parse.
* @returns A JSON object representation of the function call or its arguments.
*/
async parseResult(generations) {
const toolCalls = generations[0].message.additional_kwargs.tool_calls;
const parsedToolCalls = [];
if (!toolCalls) {
// @ts-expect-error name and arguemnts are defined by Object.defineProperty
const parsedToolCall = {
type: 'undefined',
args: {}
};
// backward-compatibility with previous
// versions of Langchain JS, which uses `name` and `arguments`
Object.defineProperty(parsedToolCall, 'name', {
get() {
return this.type;
}
});
Object.defineProperty(parsedToolCall, 'arguments', {
get() {
return this.args;
}
});
parsedToolCalls.push(parsedToolCall);
}
const clonedToolCalls = JSON.parse(JSON.stringify(toolCalls));
for (const toolCall of clonedToolCalls) {
if (toolCall.function !== undefined) {
// @ts-expect-error name and arguemnts are defined by Object.defineProperty
const parsedToolCall = {
type: toolCall.function.name,
args: JSON.parse(toolCall.function.arguments)
};
if (this.returnId) {
parsedToolCall.id = toolCall.id;
}
// backward-compatibility with previous
// versions of Langchain JS, which uses `name` and `arguments`
Object.defineProperty(parsedToolCall, 'name', {
get() {
return this.type;
}
});
Object.defineProperty(parsedToolCall, 'arguments', {
get() {
return this.args;
}
});
parsedToolCalls.push(parsedToolCall);
}
}
return parsedToolCalls;
}
}
exports.JsonOutputToolsParser = JsonOutputToolsParser;
//# sourceMappingURL=agents.js.map