From f47cf11edd6203ed2b06978a195b7353a94d948c Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Tue, 21 Nov 2023 15:26:38 -0800 Subject: [PATCH 01/11] Agent streaming --- langchain/src/agents/agent_iterator.ts | 273 +++++++++++++++++++ langchain/src/agents/executor.ts | 177 ++++++++++++ langchain/src/agents/tests/agent.int.test.ts | 40 ++- langchain/src/chains/base.ts | 26 ++ 4 files changed, 510 insertions(+), 6 deletions(-) create mode 100644 langchain/src/agents/agent_iterator.ts diff --git a/langchain/src/agents/agent_iterator.ts b/langchain/src/agents/agent_iterator.ts new file mode 100644 index 000000000000..972d10a976a0 --- /dev/null +++ b/langchain/src/agents/agent_iterator.ts @@ -0,0 +1,273 @@ +import { + CallbackManager, + CallbackManagerForChainRun, + Callbacks, +} from "../callbacks/manager.js"; +import { Serializable } from "../load/serializable.js"; +import { AgentFinish, AgentStep } from "../schema/index.js"; +import { Tool } from "../tools/base.js"; +import { AgentExecutor } from "./executor.js"; + +interface AgentExecutorIteratorInput { + agentExecutor: AgentExecutor; + inputs: Record; + callbacks?: Callbacks; + tags?: string[]; + metadata?: Record; + runName?: string; + runManager?: CallbackManagerForChainRun; +} + +export class AgentExecutorIterator + extends Serializable + implements AgentExecutorIteratorInput +{ + lc_namespace = ["langchain", "agents", "executor", "iterator"]; + + agentExecutor: AgentExecutor; + + inputs: Record; + + callbacks: Callbacks; + + tags: string[] | undefined; + + metadata: Record | undefined; + + runName: string | undefined; + + private _finalOutputs: Record | undefined; + + get finalOutputs(): Record | undefined { + return this._finalOutputs; + } + + /** Intended to be used as a setter method, needs to be async. */ + async setFinalOutputs(value: Record | undefined) { + this._finalOutputs = undefined; + if (value) { + const preparedOutputs: Record = + await this.agentExecutor.prepOutputs(this.inputs, value, true); + this._finalOutputs = preparedOutputs; + } + } + + runManager: CallbackManagerForChainRun | undefined; + + intermediateSteps: AgentStep[] = []; + + iterations = 0; + + get nameToToolMap(): Record { + const toolMap = this.agentExecutor.tools.map((tool) => ({ [tool.name]: tool })); + return Object.assign({}, ...toolMap); + } + + constructor(fields: AgentExecutorIteratorInput) { + super(); + this.agentExecutor = fields.agentExecutor; + this.inputs = fields.inputs; + this.tags = fields.tags; + this.metadata = fields.metadata; + this.runName = fields.runName; + this.runManager = fields.runManager; + } + + /** + * Reset the iterator to its initial state, clearing intermediate steps, + * iterations, and time elapsed. + */ + reset(): void { + this.intermediateSteps = []; + this.iterations = 0; + this._finalOutputs = undefined; + } + + /** + * Increment the number of iterations and update the time elapsed. + */ + updateIterations(): void { + this.iterations += 1; + } + + /** Method to initialize the iterator */ + async *[Symbol.asyncIterator]() { + 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: any) { + 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(): Promise { + if (this.iterations === 0) { + const callbackManager = await 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 + ); + } + } + + /** Not used, tbd */ + // async next(): Promise> { + // if (this.iterations === 0) { + // await this.onFirstStep(); + // } + // try { + // return this._callNext(); + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + // } catch (e: any) { + // 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; + // } + // } + + /** + * Execute the next step in the chain using the + * AgentExecutor's _takeNextStep method. + */ + async _executeNextStep( + runManager?: CallbackManagerForChainRun + ): Promise { + 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: AgentFinish | AgentStep[], + runManager?: CallbackManagerForChainRun + ): Promise> { + if ("returnValues" in nextStepOutput) { + const output = await this.agentExecutor._return( + nextStepOutput as AgentFinish, + this.intermediateSteps, + runManager + ); + if (this.runManager) { + await this.runManager.handleChainEnd(output); + } + await this.setFinalOutputs(output); + return output; + } + + this.intermediateSteps = this.intermediateSteps.concat( + nextStepOutput as AgentStep[] + ); + + let output: Record = {}; + 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 as AgentStep[] }; + return output; + } + + async _stop(): Promise> { + 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(): Promise> { + // 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; + } +} diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index 825854abfa12..6a3834c54b49 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -20,6 +20,8 @@ import { ToolInputParsingException, } from "../tools/base.js"; import { Runnable } from "../schema/runnable/base.js"; +import { RunnableConfig } from "../schema/runnable/config.js"; +import { AgentExecutorIterator } from "./agent_iterator.js"; type ExtractToolType = T extends { ToolType: infer Tool } ? Tool @@ -65,6 +67,35 @@ export class ExceptionTool extends Tool { } } +class AddableMap extends Map { + merge(other: AddableMap): AddableMap { + const result = new AddableMap(this); + for (const [key, value] of other) { + if (!result.has(key) || result.get(key) === null) { + result.set(key, value); + } else if (value !== null) { + result.set(key, result.get(key) + value); + } + } + return result; + } +} + +class AgentStreamOutput extends AddableMap { + get(key: string): any { + if (key === "intermediateSteps") { + const actions = this.get("actions") || []; + const observations = this.get("observations") || []; + return actions.map((action: any, index: number) => [ + action, + observations[index], + ]); + } else { + return super.get(key); + } + } +} + /** * A chain managing an agent using tools. * @augments BaseChain @@ -160,6 +191,10 @@ export class AgentExecutor extends BaseChain { return new AgentExecutor(fields); } + get shouldContinueGetter() { + return this.shouldContinue.bind(this); + } + /** * Method that checks if the agent execution should continue based on the * number of iterations. @@ -299,6 +334,148 @@ export class AgentExecutor extends BaseChain { return getOutput(finish); } + async _takeNextStep( + nameToolMap: Record, + inputs: ChainValues, + intermediateSteps: AgentStep[], + runManager?: CallbackManagerForChainRun + ): Promise { + let output; + try { + output = await this.agent.plan( + intermediateSteps, + inputs, + runManager?.getChild() + ); + } catch (e) { + // handle rrr + } + + if (output && "returnValues" in output) { + return output; + } + + let actions: AgentAction[]; + if (Array.isArray(output)) { + actions = output as AgentAction[]; + } else { + actions = [output as AgentAction]; + } + + const result: AgentStep[] = []; + for (const agentAction of actions) { + let observation = ""; + if (runManager) { + await runManager?.handleAgentAction(agentAction); + } + if (agentAction.tool in nameToolMap) { + const tool = nameToolMap[agentAction.tool]; + try { + observation = await tool.call( + agentAction.toolInput, + runManager?.getChild() + ); + } catch (e) { + // handle rrr + } + intermediateSteps.push({ + action: agentAction, + observation: observation ?? "", + }); + } else { + observation = `${ + agentAction.tool + } is not a valid tool, try another available tool: ${Object.keys( + nameToolMap + ).join(", ")}`; + } + result.push({ + action: agentAction, + observation: observation ?? "", + }); + } + return result; + } + + async _return( + output: AgentFinish, + intermediateSteps: AgentStep[], + runManager?: CallbackManagerForChainRun + ): Promise { + if (runManager) { + await runManager.handleAgentEnd(output); + } + const finalOutput: Record = output.returnValues; + if (this.returnIntermediateSteps) { + finalOutput.intermediateSteps = intermediateSteps; + } + return finalOutput; + } + + async _getToolReturn(nextStepOutput: AgentStep): Promise { + 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: StoppingMethod) { + if (earlyStoppingMethod === "force") { + return { + returnValues: { + output: "Agent stopped due to iteration limit or time limit.", + }, + log: "", + } as AgentFinish; + } + throw new Error( + `Got unsupported early_stopping_method: ${earlyStoppingMethod}` + ); + } + + async *_streamIterator( + inputs: Record, + /** @TODO Figure out where to use this. */ + _config: RunnableConfig | null = null + ): AsyncGenerator { + const iterator = new AgentExecutorIterator({ + inputs, + agentExecutor: this, + metadata: this.metadata, + tags: this.tags, + callbacks: this.callbacks, + }); + for await (const step of iterator) { + if (!step) { + continue; + } + if ("intermediateSteps" in step) { + const castStep = step as Record; + yield new AgentStreamOutput( + Object.entries({ + actions: castStep.intermediateSteps.map(({ action }) => action), + observations: castStep.intermediateSteps.map( + ({ observation }) => observation + ), + }) + ); + } else { + yield new AgentStreamOutput(Object.entries(step)); + } + } + } + _chainType() { return "agent_executor" as const; } diff --git a/langchain/src/agents/tests/agent.int.test.ts b/langchain/src/agents/tests/agent.int.test.ts index 1ec4a3bffca8..d8750fcece3c 100644 --- a/langchain/src/agents/tests/agent.int.test.ts +++ b/langchain/src/agents/tests/agent.int.test.ts @@ -315,14 +315,42 @@ test("Run tool web-browser", async () => { console.log(`Executing with input "${input}"...`); const result = await executor.call({ input }); - console.log( - { - result, - }, - "Run tool web-browser" - ); expect(result.intermediateSteps.length).toBeGreaterThanOrEqual(1); expect(result.intermediateSteps[0].action.tool).toEqual("web-browser"); expect(result.output).not.toEqual(""); expect(result.output).not.toEqual("Agent stopped due to max iterations."); }); + +test.only("Agent can stream", async () => { + const model = new ChatOpenAI({ + temperature: 0, + modelName: "gpt-4-1106-preview", + streaming: true, + }); + const tools = [ + new SerpAPI(process.env.SERPAPI_API_KEY, { + location: "Austin,Texas,United States", + hl: "en", + gl: "us", + }), + new Calculator(), + new WebBrowser({ model, embeddings: new OpenAIEmbeddings() }), + ]; + + const executor = await initializeAgentExecutorWithOptions(tools, model, { + agentType: "zero-shot-react-description", + returnIntermediateSteps: true, + }); + console.log("Loaded agent."); + + const input = `What is the word of the day on merriam webster`; + console.log(`Executing with input "${input}"...`); + + const result = await executor.stream({ input }); + let streamIters = 0; + for await (const item of result) { + streamIters += 1; + console.log("Stream item:", item); + } + expect(streamIters).toBeGreaterThan(1); +}); diff --git a/langchain/src/chains/base.ts b/langchain/src/chains/base.ts index c4e15cfa768c..55f48bc55d60 100644 --- a/langchain/src/chains/base.ts +++ b/langchain/src/chains/base.ts @@ -83,6 +83,32 @@ export abstract class BaseChain< return this.call(input, config); } + private _validateOutputs(outputs: Record): void { + const missingKeys = this.outputKeys.filter((k) => !(k in outputs)); + if (missingKeys.length) { + throw new Error( + `Missing output keys: ${missingKeys.join( + ", " + )} from chain ${this._chainType()}` + ); + } + } + + async prepOutputs( + inputs: Record, + outputs: Record, + returnOnlyOutputs = false + ) { + this._validateOutputs(outputs); + if (this.memory) { + await this.memory.saveContext(inputs, outputs); + } + if (returnOnlyOutputs) { + return outputs; + } + return { ...inputs, ...outputs }; + } + /** * Run the core logic of this chain and return the output */ From c8e06535f66e84d9d37991e037626225528c9a67 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Tue, 21 Nov 2023 19:57:08 -0800 Subject: [PATCH 02/11] chore: lint files --- langchain/src/agents/agent_iterator.ts | 7 +++++-- langchain/src/agents/executor.ts | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/langchain/src/agents/agent_iterator.ts b/langchain/src/agents/agent_iterator.ts index 972d10a976a0..d259c0c249ad 100644 --- a/langchain/src/agents/agent_iterator.ts +++ b/langchain/src/agents/agent_iterator.ts @@ -6,7 +6,7 @@ import { import { Serializable } from "../load/serializable.js"; import { AgentFinish, AgentStep } from "../schema/index.js"; import { Tool } from "../tools/base.js"; -import { AgentExecutor } from "./executor.js"; +import type { AgentExecutor } from "./executor.js"; interface AgentExecutorIteratorInput { agentExecutor: AgentExecutor; @@ -59,7 +59,9 @@ export class AgentExecutorIterator iterations = 0; get nameToToolMap(): Record { - const toolMap = this.agentExecutor.tools.map((tool) => ({ [tool.name]: tool })); + const toolMap = this.agentExecutor.tools.map((tool) => ({ + [tool.name]: tool, + })); return Object.assign({}, ...toolMap); } @@ -103,6 +105,7 @@ export class AgentExecutorIterator const result = await this._callNext(); yield result; + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { if ( "message" in e && diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index 6a3834c54b49..1809737b6351 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -67,6 +67,7 @@ export class ExceptionTool extends Tool { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any class AddableMap extends Map { merge(other: AddableMap): AddableMap { const result = new AddableMap(this); @@ -82,10 +83,12 @@ class AddableMap extends Map { } class AgentStreamOutput extends AddableMap { + // eslint-disable-next-line @typescript-eslint/no-explicit-any get(key: string): any { if (key === "intermediateSteps") { const actions = this.get("actions") || []; const observations = this.get("observations") || []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any return actions.map((action: any, index: number) => [ action, observations[index], @@ -445,6 +448,7 @@ export class AgentExecutor extends BaseChain { } async *_streamIterator( + // eslint-disable-next-line @typescript-eslint/no-explicit-any inputs: Record, /** @TODO Figure out where to use this. */ _config: RunnableConfig | null = null From 8567ea753a5463bba544f158fcac5cd847a7b68d Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 09:44:13 -0800 Subject: [PATCH 03/11] fix circular dep issue --- langchain/src/agents/agent_iterator.ts | 276 ------------------------ langchain/src/agents/executor.ts | 279 ++++++++++++++++++++++++- 2 files changed, 275 insertions(+), 280 deletions(-) delete mode 100644 langchain/src/agents/agent_iterator.ts diff --git a/langchain/src/agents/agent_iterator.ts b/langchain/src/agents/agent_iterator.ts deleted file mode 100644 index d259c0c249ad..000000000000 --- a/langchain/src/agents/agent_iterator.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { - CallbackManager, - CallbackManagerForChainRun, - Callbacks, -} from "../callbacks/manager.js"; -import { Serializable } from "../load/serializable.js"; -import { AgentFinish, AgentStep } from "../schema/index.js"; -import { Tool } from "../tools/base.js"; -import type { AgentExecutor } from "./executor.js"; - -interface AgentExecutorIteratorInput { - agentExecutor: AgentExecutor; - inputs: Record; - callbacks?: Callbacks; - tags?: string[]; - metadata?: Record; - runName?: string; - runManager?: CallbackManagerForChainRun; -} - -export class AgentExecutorIterator - extends Serializable - implements AgentExecutorIteratorInput -{ - lc_namespace = ["langchain", "agents", "executor", "iterator"]; - - agentExecutor: AgentExecutor; - - inputs: Record; - - callbacks: Callbacks; - - tags: string[] | undefined; - - metadata: Record | undefined; - - runName: string | undefined; - - private _finalOutputs: Record | undefined; - - get finalOutputs(): Record | undefined { - return this._finalOutputs; - } - - /** Intended to be used as a setter method, needs to be async. */ - async setFinalOutputs(value: Record | undefined) { - this._finalOutputs = undefined; - if (value) { - const preparedOutputs: Record = - await this.agentExecutor.prepOutputs(this.inputs, value, true); - this._finalOutputs = preparedOutputs; - } - } - - runManager: CallbackManagerForChainRun | undefined; - - intermediateSteps: AgentStep[] = []; - - iterations = 0; - - get nameToToolMap(): Record { - const toolMap = this.agentExecutor.tools.map((tool) => ({ - [tool.name]: tool, - })); - return Object.assign({}, ...toolMap); - } - - constructor(fields: AgentExecutorIteratorInput) { - super(); - this.agentExecutor = fields.agentExecutor; - this.inputs = fields.inputs; - this.tags = fields.tags; - this.metadata = fields.metadata; - this.runName = fields.runName; - this.runManager = fields.runManager; - } - - /** - * Reset the iterator to its initial state, clearing intermediate steps, - * iterations, and time elapsed. - */ - reset(): void { - this.intermediateSteps = []; - this.iterations = 0; - this._finalOutputs = undefined; - } - - /** - * Increment the number of iterations and update the time elapsed. - */ - updateIterations(): void { - this.iterations += 1; - } - - /** Method to initialize the iterator */ - async *[Symbol.asyncIterator]() { - this.reset(); - - // Loop to handle iteration - while (true) { - try { - if (this.iterations === 0) { - await this.onFirstStep(); - } - - const result = await this._callNext(); - yield result; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { - 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(): Promise { - if (this.iterations === 0) { - const callbackManager = await 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 - ); - } - } - - /** Not used, tbd */ - // async next(): Promise> { - // if (this.iterations === 0) { - // await this.onFirstStep(); - // } - // try { - // return this._callNext(); - // // eslint-disable-next-line @typescript-eslint/no-explicit-any - // } catch (e: any) { - // 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; - // } - // } - - /** - * Execute the next step in the chain using the - * AgentExecutor's _takeNextStep method. - */ - async _executeNextStep( - runManager?: CallbackManagerForChainRun - ): Promise { - 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: AgentFinish | AgentStep[], - runManager?: CallbackManagerForChainRun - ): Promise> { - if ("returnValues" in nextStepOutput) { - const output = await this.agentExecutor._return( - nextStepOutput as AgentFinish, - this.intermediateSteps, - runManager - ); - if (this.runManager) { - await this.runManager.handleChainEnd(output); - } - await this.setFinalOutputs(output); - return output; - } - - this.intermediateSteps = this.intermediateSteps.concat( - nextStepOutput as AgentStep[] - ); - - let output: Record = {}; - 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 as AgentStep[] }; - return output; - } - - async _stop(): Promise> { - 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(): Promise> { - // 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; - } -} diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index 1809737b6351..4074de90d2d4 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -8,20 +8,291 @@ import { StoppingMethod } from "./types.js"; import { SerializedLLMChain } from "../chains/serde.js"; import { AgentAction, + ChainValues, AgentFinish, AgentStep, - ChainValues, } from "../schema/index.js"; -import { CallbackManagerForChainRun } from "../callbacks/manager.js"; +import { + CallbackManager, + CallbackManagerForChainRun, + Callbacks, +} from "../callbacks/manager.js"; import { OutputParserException } from "../schema/output_parser.js"; import { StructuredTool, - Tool, ToolInputParsingException, + Tool, } from "../tools/base.js"; import { Runnable } from "../schema/runnable/base.js"; import { RunnableConfig } from "../schema/runnable/config.js"; -import { AgentExecutorIterator } from "./agent_iterator.js"; +import { Serializable } from "../load/serializable.js"; + +interface AgentExecutorIteratorInput { + agentExecutor: AgentExecutor; + inputs: Record; + callbacks?: Callbacks; + tags?: string[]; + metadata?: Record; + runName?: string; + runManager?: CallbackManagerForChainRun; +} + +export class AgentExecutorIterator + extends Serializable + implements AgentExecutorIteratorInput +{ + lc_namespace = ["langchain", "agents", "executor", "iterator"]; + + agentExecutor: AgentExecutor; + + inputs: Record; + + callbacks: Callbacks; + + tags: string[] | undefined; + + metadata: Record | undefined; + + runName: string | undefined; + + private _finalOutputs: Record | undefined; + + get finalOutputs(): Record | undefined { + return this._finalOutputs; + } + + /** Intended to be used as a setter method, needs to be async. */ + async setFinalOutputs(value: Record | undefined) { + this._finalOutputs = undefined; + if (value) { + const preparedOutputs: Record = + await this.agentExecutor.prepOutputs(this.inputs, value, true); + this._finalOutputs = preparedOutputs; + } + } + + runManager: CallbackManagerForChainRun | undefined; + + intermediateSteps: AgentStep[] = []; + + iterations = 0; + + get nameToToolMap(): Record { + const toolMap = this.agentExecutor.tools.map((tool) => ({ + [tool.name]: tool, + })); + return Object.assign({}, ...toolMap); + } + + constructor(fields: AgentExecutorIteratorInput) { + super(); + this.agentExecutor = fields.agentExecutor; + this.inputs = fields.inputs; + this.tags = fields.tags; + this.metadata = fields.metadata; + this.runName = fields.runName; + this.runManager = fields.runManager; + } + + /** + * Reset the iterator to its initial state, clearing intermediate steps, + * iterations, and time elapsed. + */ + reset(): void { + this.intermediateSteps = []; + this.iterations = 0; + this._finalOutputs = undefined; + } + + /** + * Increment the number of iterations and update the time elapsed. + */ + updateIterations(): void { + this.iterations += 1; + } + + /** Method to initialize the iterator */ + async *[Symbol.asyncIterator]() { + this.reset(); + + // Loop to handle iteration + while (true) { + try { + if (this.iterations === 0) { + await this.onFirstStep(); + } + + const result = await this._callNext(); + yield result; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + 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(): Promise { + if (this.iterations === 0) { + const callbackManager = await 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 + ); + } + } + + /** Not used, tbd */ + // async next(): Promise> { + // if (this.iterations === 0) { + // await this.onFirstStep(); + // } + // try { + // return this._callNext(); + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + // } catch (e: any) { + // 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; + // } + // } + + /** + * Execute the next step in the chain using the + * AgentExecutor's _takeNextStep method. + */ + async _executeNextStep( + runManager?: CallbackManagerForChainRun + ): Promise { + 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: AgentFinish | AgentStep[], + runManager?: CallbackManagerForChainRun + ): Promise> { + if ("returnValues" in nextStepOutput) { + const output = await this.agentExecutor._return( + nextStepOutput as AgentFinish, + this.intermediateSteps, + runManager + ); + if (this.runManager) { + await this.runManager.handleChainEnd(output); + } + await this.setFinalOutputs(output); + return output; + } + + this.intermediateSteps = this.intermediateSteps.concat( + nextStepOutput as AgentStep[] + ); + + let output: Record = {}; + 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 as AgentStep[] }; + return output; + } + + async _stop(): Promise> { + 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(): Promise> { + // 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; + } +} type ExtractToolType = T extends { ToolType: infer Tool } ? Tool From 8835030f216c36185b6604b9fb0a63f5b27ed25e Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 09:51:52 -0800 Subject: [PATCH 04/11] error handling --- langchain/src/agents/executor.ts | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index 4074de90d2d4..b6f785c27e44 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -85,7 +85,7 @@ export class AgentExecutorIterator } constructor(fields: AgentExecutorIteratorInput) { - super(); + super(fields); this.agentExecutor = fields.agentExecutor; this.inputs = fields.inputs; this.tags = fields.tags; @@ -622,7 +622,32 @@ export class AgentExecutor extends BaseChain { runManager?.getChild() ); } catch (e) { - // handle rrr + // eslint-disable-next-line no-instanceof/no-instanceof + if (e instanceof 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, + } as AgentAction; + } else { + throw e; + } } if (output && "returnValues" in output) { From e4e2252e53697f315d8c147a9f725bde838bfc4d Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 12:20:36 -0800 Subject: [PATCH 05/11] cr --- langchain/src/agents/executor.ts | 72 +++++++++----------- langchain/src/agents/tests/agent.int.test.ts | 23 ++++++- 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index b6f785c27e44..2704f70d5f00 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -24,7 +24,6 @@ import { Tool, } from "../tools/base.js"; import { Runnable } from "../schema/runnable/base.js"; -import { RunnableConfig } from "../schema/runnable/config.js"; import { Serializable } from "../load/serializable.js"; interface AgentExecutorIteratorInput { @@ -339,34 +338,23 @@ export class ExceptionTool extends Tool { } // eslint-disable-next-line @typescript-eslint/no-explicit-any -class AddableMap extends Map { - merge(other: AddableMap): AddableMap { - const result = new AddableMap(this); - for (const [key, value] of other) { - if (!result.has(key) || result.get(key) === null) { - result.set(key, value); - } else if (value !== null) { - result.set(key, result.get(key) + value); - } - } - return result; +class AddableObject { + data: Record; + + constructor(initialData: Record = {}) { + this.data = initialData; } -} -class AgentStreamOutput extends AddableMap { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get(key: string): any { - if (key === "intermediateSteps") { - const actions = this.get("actions") || []; - const observations = this.get("observations") || []; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return actions.map((action: any, index: number) => [ - action, - observations[index], - ]); - } else { - return super.get(key); + merge(other: AddableObject): AddableObject { + const result = new AddableObject(this.data); + for (const key in other.data) { + if (!(key in result.data) || result.data[key] === null) { + result.data[key] = other.data[key]; + } else if (other.data[key] !== null) { + result.data[key] += other.data[key]; + } } + return result; } } @@ -407,6 +395,8 @@ export class AgentExecutor extends BaseChain { earlyStoppingMethod: StoppingMethod = "force"; + addableObject: AddableObject = new AddableObject(); + /** * How to handle errors raised by the agent's output parser. Defaults to `False`, which raises the error. @@ -745,10 +735,8 @@ export class AgentExecutor extends BaseChain { async *_streamIterator( // eslint-disable-next-line @typescript-eslint/no-explicit-any - inputs: Record, - /** @TODO Figure out where to use this. */ - _config: RunnableConfig | null = null - ): AsyncGenerator { + inputs: Record + ): AsyncGenerator { const iterator = new AgentExecutorIterator({ inputs, agentExecutor: this, @@ -761,17 +749,25 @@ export class AgentExecutor extends BaseChain { continue; } if ("intermediateSteps" in step) { - const castStep = step as Record; - yield new AgentStreamOutput( - Object.entries({ - actions: castStep.intermediateSteps.map(({ action }) => action), - observations: castStep.intermediateSteps.map( + const { output } = step; + const intermediateSteps = (step.intermediateSteps as AgentStep[]).map( + (step) => ({ action: step.action, observation: step.observation }) + ); + if (output) { + yield { + output: output as string, + intermediateSteps, + }; + } else { + yield { + actions: intermediateSteps.map(({ action }) => action), + observations: intermediateSteps.map( ({ observation }) => observation ), - }) - ); + }; + } } else { - yield new AgentStreamOutput(Object.entries(step)); + yield step; } } } diff --git a/langchain/src/agents/tests/agent.int.test.ts b/langchain/src/agents/tests/agent.int.test.ts index d8750fcece3c..b993a14c6747 100644 --- a/langchain/src/agents/tests/agent.int.test.ts +++ b/langchain/src/agents/tests/agent.int.test.ts @@ -12,7 +12,7 @@ import { Tool } from "../../tools/base.js"; import { ChatOpenAI } from "../../chat_models/openai.js"; import { RunnableSequence } from "../../schema/runnable/base.js"; import { OutputParserException } from "../../schema/output_parser.js"; -import { AIMessage } from "../../schema/index.js"; +import { AIMessage, AgentStep } from "../../schema/index.js"; test("Run agent from hub", async () => { const model = new OpenAI({ temperature: 0, modelName: "text-babbage-001" }); @@ -316,12 +316,13 @@ test("Run tool web-browser", async () => { const result = await executor.call({ input }); expect(result.intermediateSteps.length).toBeGreaterThanOrEqual(1); - expect(result.intermediateSteps[0].action.tool).toEqual("web-browser"); + expect(result.intermediateSteps[0].action.tool).toEqual("search"); + expect(result.intermediateSteps[1].action.tool).toEqual("web-browser"); expect(result.output).not.toEqual(""); expect(result.output).not.toEqual("Agent stopped due to max iterations."); }); -test.only("Agent can stream", async () => { +test("Agent can stream", async () => { const model = new ChatOpenAI({ temperature: 0, modelName: "gpt-4-1106-preview", @@ -348,9 +349,25 @@ test.only("Agent can stream", async () => { const result = await executor.stream({ input }); let streamIters = 0; + let finalResponse: any; for await (const item of result) { streamIters += 1; console.log("Stream item:", item); + // each stream contains the previous steps, + // so we can overwrite on each stream. + finalResponse = item; } + + console.log("__finalResponse__", finalResponse); + + expect("intermediateSteps" in finalResponse).toBeTruthy(); + expect("output" in finalResponse).toBeTruthy(); + + expect(finalResponse.intermediateSteps.length).toBeGreaterThan(1); + const toolsUsed: Array = finalResponse.intermediateSteps.map( + (step: AgentStep) => step.action.tool + ); expect(streamIters).toBeGreaterThan(1); + // the last tool used should be the web-browser + expect(toolsUsed?.[toolsUsed.length - 1]).toEqual("web-browser"); }); From f2b4ba6b301352ae3b1719dfc156aa0835539b39 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 13:52:52 -0800 Subject: [PATCH 06/11] cr --- langchain/src/agents/executor.ts | 136 ++++++++----------- langchain/src/agents/tests/agent.int.test.ts | 9 +- 2 files changed, 63 insertions(+), 82 deletions(-) diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index 2704f70d5f00..3df37d22f658 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -40,7 +40,7 @@ export class AgentExecutorIterator extends Serializable implements AgentExecutorIteratorInput { - lc_namespace = ["langchain", "agents", "executor", "iterator"]; + lc_namespace = ["langchain", "agents", "executor_iterator"]; agentExecutor: AgentExecutor; @@ -110,8 +110,7 @@ export class AgentExecutorIterator this.iterations += 1; } - /** Method to initialize the iterator */ - async *[Symbol.asyncIterator]() { + async *streamIterator() { this.reset(); // Loop to handle iteration @@ -142,6 +141,38 @@ export class AgentExecutorIterator } } + /** Method to initialize the iterator */ + // async *[Symbol.asyncIterator]() { + // this.reset(); + + // // Loop to handle iteration + // while (true) { + // try { + // if (this.iterations === 0) { + // await this.onFirstStep(); + // } + + // const result = await this._callNext(); + // yield result; + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + // } catch (e: any) { + // 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(): Promise { if (this.iterations === 0) { @@ -168,31 +199,6 @@ export class AgentExecutorIterator } } - /** Not used, tbd */ - // async next(): Promise> { - // if (this.iterations === 0) { - // await this.onFirstStep(); - // } - // try { - // return this._callNext(); - // // eslint-disable-next-line @typescript-eslint/no-explicit-any - // } catch (e: any) { - // 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; - // } - // } - /** * Execute the next step in the chain using the * AgentExecutor's _takeNextStep method. @@ -337,27 +343,6 @@ export class ExceptionTool extends Tool { } } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -class AddableObject { - data: Record; - - constructor(initialData: Record = {}) { - this.data = initialData; - } - - merge(other: AddableObject): AddableObject { - const result = new AddableObject(this.data); - for (const key in other.data) { - if (!(key in result.data) || result.data[key] === null) { - result.data[key] = other.data[key]; - } else if (other.data[key] !== null) { - result.data[key] += other.data[key]; - } - } - return result; - } -} - /** * A chain managing an agent using tools. * @augments BaseChain @@ -395,8 +380,6 @@ export class AgentExecutor extends BaseChain { earlyStoppingMethod: StoppingMethod = "force"; - addableObject: AddableObject = new AddableObject(); - /** * How to handle errors raised by the agent's output parser. Defaults to `False`, which raises the error. @@ -640,7 +623,7 @@ export class AgentExecutor extends BaseChain { } } - if (output && "returnValues" in output) { + if ("returnValues" in output) { return output; } @@ -665,12 +648,24 @@ export class AgentExecutor extends BaseChain { runManager?.getChild() ); } catch (e) { - // handle rrr + // eslint-disable-next-line no-instanceof/no-instanceof + if (e instanceof 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() + ); + } } - intermediateSteps.push({ - action: agentAction, - observation: observation ?? "", - }); } else { observation = `${ agentAction.tool @@ -680,7 +675,7 @@ export class AgentExecutor extends BaseChain { } result.push({ action: agentAction, - observation: observation ?? "", + observation, }); } return result; @@ -737,38 +732,19 @@ export class AgentExecutor extends BaseChain { // eslint-disable-next-line @typescript-eslint/no-explicit-any inputs: Record ): AsyncGenerator { - const iterator = new AgentExecutorIterator({ + const agentExecutorIterator = new AgentExecutorIterator({ inputs, agentExecutor: this, metadata: this.metadata, tags: this.tags, callbacks: this.callbacks, }); + const iterator = agentExecutorIterator.streamIterator(); for await (const step of iterator) { if (!step) { continue; } - if ("intermediateSteps" in step) { - const { output } = step; - const intermediateSteps = (step.intermediateSteps as AgentStep[]).map( - (step) => ({ action: step.action, observation: step.observation }) - ); - if (output) { - yield { - output: output as string, - intermediateSteps, - }; - } else { - yield { - actions: intermediateSteps.map(({ action }) => action), - observations: intermediateSteps.map( - ({ observation }) => observation - ), - }; - } - } else { - yield step; - } + yield step; } } diff --git a/langchain/src/agents/tests/agent.int.test.ts b/langchain/src/agents/tests/agent.int.test.ts index b993a14c6747..9c87318f75eb 100644 --- a/langchain/src/agents/tests/agent.int.test.ts +++ b/langchain/src/agents/tests/agent.int.test.ts @@ -315,6 +315,12 @@ test("Run tool web-browser", async () => { console.log(`Executing with input "${input}"...`); const result = await executor.call({ input }); + console.log( + { + result, + }, + "Run tool web-browser" + ); expect(result.intermediateSteps.length).toBeGreaterThanOrEqual(1); expect(result.intermediateSteps[0].action.tool).toEqual("search"); expect(result.intermediateSteps[1].action.tool).toEqual("web-browser"); @@ -363,11 +369,10 @@ test("Agent can stream", async () => { expect("intermediateSteps" in finalResponse).toBeTruthy(); expect("output" in finalResponse).toBeTruthy(); - expect(finalResponse.intermediateSteps.length).toBeGreaterThan(1); + expect(streamIters).toBeGreaterThan(1); const toolsUsed: Array = finalResponse.intermediateSteps.map( (step: AgentStep) => step.action.tool ); - expect(streamIters).toBeGreaterThan(1); // the last tool used should be the web-browser expect(toolsUsed?.[toolsUsed.length - 1]).toEqual("web-browser"); }); From 8d04fd61d1e1b811d57a96674626739c9e8d2e82 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 13:55:40 -0800 Subject: [PATCH 07/11] fix any eslint --- langchain/src/agents/tests/agent.int.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/langchain/src/agents/tests/agent.int.test.ts b/langchain/src/agents/tests/agent.int.test.ts index 9c87318f75eb..c600768cecc3 100644 --- a/langchain/src/agents/tests/agent.int.test.ts +++ b/langchain/src/agents/tests/agent.int.test.ts @@ -355,6 +355,7 @@ test("Agent can stream", async () => { const result = await executor.stream({ input }); let streamIters = 0; + // eslint-disable-next-line @typescript-eslint/no-explicit-any let finalResponse: any; for await (const item of result) { streamIters += 1; From 3c707f6c92f777d552fb7499922b95e37138f478 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 13:56:27 -0800 Subject: [PATCH 08/11] rm commented out method --- langchain/src/agents/executor.ts | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index 3df37d22f658..d005cf64cf91 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -141,38 +141,6 @@ export class AgentExecutorIterator } } - /** Method to initialize the iterator */ - // async *[Symbol.asyncIterator]() { - // this.reset(); - - // // Loop to handle iteration - // while (true) { - // try { - // if (this.iterations === 0) { - // await this.onFirstStep(); - // } - - // const result = await this._callNext(); - // yield result; - // // eslint-disable-next-line @typescript-eslint/no-explicit-any - // } catch (e: any) { - // 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(): Promise { if (this.iterations === 0) { From 26aab23fc64ea6519b5ff6129029b9c3b24ec5b4 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 13:59:04 -0800 Subject: [PATCH 09/11] Update langchain/src/agents/executor.ts --- langchain/src/agents/executor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index d005cf64cf91..aa508b4d1e35 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -95,7 +95,7 @@ export class AgentExecutorIterator /** * Reset the iterator to its initial state, clearing intermediate steps, - * iterations, and time elapsed. + * iterations, and the final output. */ reset(): void { this.intermediateSteps = []; From cca12a753991ea2d8dc5354beedff6a3222f1eec Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 13:59:08 -0800 Subject: [PATCH 10/11] Update langchain/src/agents/executor.ts --- langchain/src/agents/executor.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index aa508b4d1e35..4337896b5410 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -103,9 +103,6 @@ export class AgentExecutorIterator this._finalOutputs = undefined; } - /** - * Increment the number of iterations and update the time elapsed. - */ updateIterations(): void { this.iterations += 1; } From ebfc99d92404373240ef95c49371931503f2a521 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 22 Nov 2023 14:00:45 -0800 Subject: [PATCH 11/11] cr --- langchain/src/agents/executor.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/langchain/src/agents/executor.ts b/langchain/src/agents/executor.ts index 4337896b5410..bc2191a4b6e7 100644 --- a/langchain/src/agents/executor.ts +++ b/langchain/src/agents/executor.ts @@ -138,7 +138,10 @@ export class AgentExecutorIterator } } - /** Perform any necessary setup for the first step of the asynchronous iterator. */ + /** + * Perform any necessary setup for the first step + * of the asynchronous iterator. + */ async onFirstStep(): Promise { if (this.iterations === 0) { const callbackManager = await CallbackManager.configure(