From df9a9f1f833502f2df6f6fd19450cc4030d4dad6 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Tue, 28 Nov 2023 16:07:23 -0800 Subject: [PATCH 01/25] Runnable with message history --- langchain-core/src/callbacks/manager.ts | 7 + langchain-core/src/chat_history.ts | 34 ++++ langchain-core/src/runnables/history.ts | 178 ++++++++++++++++++ .../tests/runnable_history.int.test.ts | 44 +++++ 4 files changed, 263 insertions(+) create mode 100644 langchain-core/src/runnables/history.ts create mode 100644 langchain-core/src/runnables/tests/runnable_history.int.test.ts diff --git a/langchain-core/src/callbacks/manager.ts b/langchain-core/src/callbacks/manager.ts index 34fe086636a6..66e6d7edebf3 100644 --- a/langchain-core/src/callbacks/manager.ts +++ b/langchain-core/src/callbacks/manager.ts @@ -61,6 +61,13 @@ export interface BaseCallbackConfig { * Tags are passed to all callbacks, metadata is passed to handle*Start callbacks. */ callbacks?: Callbacks; + + /** + * Runtime values for attributes previously made configurable on this Runnable, + * or sub-Runnables. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + configurable?: Record; } export function parseCallbackConfigArg( diff --git a/langchain-core/src/chat_history.ts b/langchain-core/src/chat_history.ts index 6841d19e865e..7c17fc2f5423 100644 --- a/langchain-core/src/chat_history.ts +++ b/langchain-core/src/chat_history.ts @@ -32,3 +32,37 @@ export abstract class BaseListChatMessageHistory extends Serializable { return this.addMessage(new AIMessage(message)); } } + +export class FakeChatMessageHistory extends BaseChatMessageHistory { + lc_namespace = ["langchain", "core", "message", "fake"]; + + messages: Array = []; + + constructor() { + super(); + } + + public getMessages(): Promise { + return Promise.resolve(this.messages); + } + + public addMessage(message: BaseMessage): Promise { + this.messages.push(message); + return Promise.resolve(); + } + + public addUserMessage(message: string): Promise { + this.messages.push(new HumanMessage(message)); + return Promise.resolve(); + } + + public addAIChatMessage(message: string): Promise { + this.messages.push(new AIMessage(message)); + return Promise.resolve(); + } + + public clear(): Promise { + this.messages = []; + return Promise.resolve(); + } +} diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts new file mode 100644 index 000000000000..354bf91d567e --- /dev/null +++ b/langchain-core/src/runnables/history.ts @@ -0,0 +1,178 @@ +import { BaseChatMessageHistory } from "../chat_history.js"; +import { + AIMessage, + BaseMessage, + HumanMessage, + isBaseMessage, +} from "../messages/index.js"; +import { Run } from "../tracers/base.js"; +import { + Runnable, + RunnableBinding, + RunnableBindingArgs, + RunnableLambda, +} from "./base.js"; +import { RunnableConfig } from "./config.js"; +import { RunnablePassthrough } from "./passthrough.js"; + +type GetSessionHistoryCallable = ( + ...args: Array +) => BaseChatMessageHistory; + +export class RunnableWithMessageHistory< + RunInput, + RunOutput, + CallOptions extends RunnableConfig +> extends RunnableBinding { + runnable: Runnable; + + inputMessagesKey = "input"; + + outputMessagesKey = "output"; + + historyMessagesKey = "history"; + + getMessageHistory: GetSessionHistoryCallable; + + constructor( + fields: RunnableBindingArgs & { + runnable: Runnable; + getMessageHistory: GetSessionHistoryCallable; + inputMessagesKey?: string; + outputMessagesKey?: string; + historyMessagesKey?: string; + } + ) { + super(fields); + this.runnable = fields.runnable; + this.getMessageHistory = fields.getMessageHistory; + this.inputMessagesKey = fields.inputMessagesKey ?? this.inputMessagesKey; + this.outputMessagesKey = fields.outputMessagesKey ?? this.outputMessagesKey; + this.historyMessagesKey = + fields.historyMessagesKey ?? this.historyMessagesKey; + + let historyChain: Runnable = new RunnableLambda({ + func: (input: any) => (config: CallOptions) => + this._enterHistory(input, config), + }).withConfig({ runName: "load_history" }); + + const messages_key = + (fields.historyMessagesKey ?? this.historyMessagesKey) || + (fields.inputMessagesKey ?? this.inputMessagesKey); + if (messages_key) { + historyChain = RunnablePassthrough.assign({ + [messages_key]: historyChain, + }).withConfig({ runName: "insert_history" }); + } + + // const bound = historyChain + // .pipe(fields.runnable) + // .withConfig({ runName: "RunnableWithMessageHistory" }) + + // this.bound = bound; + } + + _getInputMessages( + inputValue: string | BaseMessage | Array + ): Array { + if (typeof inputValue === "string") { + return [new HumanMessage(inputValue)]; + } else if (Array.isArray(inputValue)) { + return inputValue; + } else { + return [inputValue]; + } + } + + _getOutputMessages( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + outputValue: string | BaseMessage | Array | Record + ): Array { + let newOutputValue = outputValue; + if ( + !Array.isArray(outputValue) && + !isBaseMessage(outputValue) && + typeof outputValue !== "string" + ) { + newOutputValue = outputValue[this.outputMessagesKey]; + } + + if (typeof newOutputValue === "string") { + return [new AIMessage(newOutputValue)]; + } else if (Array.isArray(newOutputValue)) { + return newOutputValue; + } else if (isBaseMessage(newOutputValue)) { + return [newOutputValue]; + } + throw new Error( + `Expected a string, BaseMessage, or array of BaseMessages. Received: ${JSON.stringify( + newOutputValue, + null, + 2 + )}` + ); + } + + _enterHistory(input: any, config: CallOptions): Array { + // what the hell is python doin? + const history = config.configurable?.messageHistory; + + if (this.historyMessagesKey) { + // from python, once again, what the?? + // return history.messages.copy(); + return history.messages; + } + + const inputVal = this.inputMessagesKey + ? input[this.inputMessagesKey] + : input; + return [...history.messages, ...this._getInputMessages(inputVal)]; + } + + _exitHistory(run: Run, config: CallOptions): void { + // what the hell is python doin? + const history = config.configurable?.messageHistory; + + // Get input messages + const { inputs } = run; + const inputValue = inputs[this.inputMessagesKey]; + const inputMessages = this._getInputMessages(inputValue); + // Get output messages + const outputValue = run.outputs; + if (!outputValue) { + throw new Error( + `Output values from 'Run' undefined. Run: ${JSON.stringify( + run, + null, + 2 + )}` + ); + } + const outputMessages = this._getOutputMessages(outputValue); + + for (const message of [...inputMessages, ...outputMessages]) { + history.addMessage(message); + } + } + + _mergeConfigs(...configs: Array) { + const config = super._mergeConfig(...configs); + // Extract sessionId + if (!config.configurable || !config.configurable.sessionId) { + const exampleInput = { + [this.inputMessagesKey]: "foo", + }; + const exampleConfig = { configurable: { sessionId: "123" } }; + throw new Error( + `session_id is required. Pass it in as part of the config argument to .invoke() or .stream()\n` + + `eg. chain.invoke(${JSON.stringify(exampleInput)}, ${JSON.stringify( + exampleConfig + )})` + ); + } + // attach messageHistory + const { sessionId } = config.configurable; + config.configurable.messageHistory = this.getMessageHistory(sessionId); + return config; + } +} diff --git a/langchain-core/src/runnables/tests/runnable_history.int.test.ts b/langchain-core/src/runnables/tests/runnable_history.int.test.ts new file mode 100644 index 000000000000..70680e574acb --- /dev/null +++ b/langchain-core/src/runnables/tests/runnable_history.int.test.ts @@ -0,0 +1,44 @@ +import { HumanMessage, isBaseMessage } from "../../messages/index.js"; +import { RunnableLambda } from "../base.js"; +import { RunnableConfig } from "../config.js"; +import { RunnableWithMessageHistory } from "../history.js"; +import { + BaseChatMessageHistory, + FakeChatMessageHistory, +} from "../../chat_history.js"; + +function getGetSessionHistory(): (sessionId: string) => BaseChatMessageHistory { + const chatHistoryStore: { [key: string]: BaseChatMessageHistory } = {}; + + function getSessionHistory(sessionId: string): BaseChatMessageHistory { + if (!(sessionId in chatHistoryStore)) { + chatHistoryStore[sessionId] = new FakeChatMessageHistory(); + } + return chatHistoryStore[sessionId]; + } + + return getSessionHistory; +} + +test("Runnable with message history", async () => { + const runnable = new RunnableLambda({ + func: (messages: HumanMessage[]) => + `you said: ${messages + .filter((m) => isBaseMessage(m)) + .map((m) => m.content) + .join("\n")}`, + }); + const getSessionHistory = getGetSessionHistory(); + const withHistory = new RunnableWithMessageHistory({ + runnable, + bound: runnable, + kwargs: {}, + config: {}, + getMessageHistory: getSessionHistory, + }); + const config: RunnableConfig = { configurable: { session_id: "1" } }; + let output = await withHistory.invoke([new HumanMessage("hello")], config); + expect(output).toBe("you said: hello"); + output = await withHistory.invoke([new HumanMessage("good bye")], config); + expect(output).toBe("you said: hello\ngood bye"); +}); From a22855ef1dff7d8b1eac1117af65b16313644e82 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Tue, 28 Nov 2023 16:09:26 -0800 Subject: [PATCH 02/25] cr --- langchain-core/src/runnables/history.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index 354bf91d567e..4a298f573226 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -16,6 +16,7 @@ import { RunnableConfig } from "./config.js"; import { RunnablePassthrough } from "./passthrough.js"; type GetSessionHistoryCallable = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any ...args: Array ) => BaseChatMessageHistory; @@ -52,6 +53,7 @@ export class RunnableWithMessageHistory< fields.historyMessagesKey ?? this.historyMessagesKey; let historyChain: Runnable = new RunnableLambda({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any func: (input: any) => (config: CallOptions) => this._enterHistory(input, config), }).withConfig({ runName: "load_history" }); @@ -113,13 +115,12 @@ export class RunnableWithMessageHistory< ); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any _enterHistory(input: any, config: CallOptions): Array { - // what the hell is python doin? const history = config.configurable?.messageHistory; if (this.historyMessagesKey) { - // from python, once again, what the?? - // return history.messages.copy(); + // todo: this is def not right brace! return history.messages; } @@ -130,7 +131,6 @@ export class RunnableWithMessageHistory< } _exitHistory(run: Run, config: CallOptions): void { - // what the hell is python doin? const history = config.configurable?.messageHistory; // Get input messages From add083febb7af7dda5c317ab6100060358685582 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Tue, 28 Nov 2023 17:34:53 -0800 Subject: [PATCH 03/25] cr --- langchain-core/src/runnables/history.ts | 85 ++++++++++--------- .../tests/runnable_history.int.test.ts | 28 ++++-- 2 files changed, 65 insertions(+), 48 deletions(-) diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index 4a298f573226..c88a2f1f35e2 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -1,3 +1,4 @@ +import { BaseCallbackConfig } from "../callbacks/manager.js"; import { BaseChatMessageHistory } from "../chat_history.js"; import { AIMessage, @@ -12,31 +13,29 @@ import { RunnableBindingArgs, RunnableLambda, } from "./base.js"; -import { RunnableConfig } from "./config.js"; import { RunnablePassthrough } from "./passthrough.js"; type GetSessionHistoryCallable = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any ...args: Array -) => BaseChatMessageHistory; +) => Promise; export class RunnableWithMessageHistory< RunInput, - RunOutput, - CallOptions extends RunnableConfig -> extends RunnableBinding { + RunOutput +> extends RunnableBinding { runnable: Runnable; - inputMessagesKey = "input"; + inputMessagesKey?: string; - outputMessagesKey = "output"; + outputMessagesKey?: string; - historyMessagesKey = "history"; + historyMessagesKey?: string; getMessageHistory: GetSessionHistoryCallable; constructor( - fields: RunnableBindingArgs & { + fields: RunnableBindingArgs & { runnable: Runnable; getMessageHistory: GetSessionHistoryCallable; inputMessagesKey?: string; @@ -47,31 +46,30 @@ export class RunnableWithMessageHistory< super(fields); this.runnable = fields.runnable; this.getMessageHistory = fields.getMessageHistory; - this.inputMessagesKey = fields.inputMessagesKey ?? this.inputMessagesKey; - this.outputMessagesKey = fields.outputMessagesKey ?? this.outputMessagesKey; - this.historyMessagesKey = - fields.historyMessagesKey ?? this.historyMessagesKey; + this.inputMessagesKey = fields.inputMessagesKey; + this.outputMessagesKey = fields.outputMessagesKey; + this.historyMessagesKey = fields.historyMessagesKey; let historyChain: Runnable = new RunnableLambda({ // eslint-disable-next-line @typescript-eslint/no-explicit-any - func: (input: any) => (config: CallOptions) => - this._enterHistory(input, config), - }).withConfig({ runName: "load_history" }); - - const messages_key = - (fields.historyMessagesKey ?? this.historyMessagesKey) || - (fields.inputMessagesKey ?? this.inputMessagesKey); - if (messages_key) { + func: (input: any) => { + console.log("HISTORY CHAIN CALLED", input); + return this._enterHistory(input, {}); + }, + }).withConfig({ runName: "loadHistory" }); + + const messagesKey = fields.historyMessagesKey || fields.inputMessagesKey; + if (messagesKey) { historyChain = RunnablePassthrough.assign({ - [messages_key]: historyChain, - }).withConfig({ runName: "insert_history" }); + [messagesKey]: historyChain, + }).withConfig({ runName: "insertHistory" }); } - // const bound = historyChain - // .pipe(fields.runnable) - // .withConfig({ runName: "RunnableWithMessageHistory" }) + const bound = historyChain + .pipe(fields.runnable) + .withConfig({ runName: "RunnableWithMessageHistory" }); - // this.bound = bound; + this.bound = bound; } _getInputMessages( @@ -96,7 +94,7 @@ export class RunnableWithMessageHistory< !isBaseMessage(outputValue) && typeof outputValue !== "string" ) { - newOutputValue = outputValue[this.outputMessagesKey]; + newOutputValue = outputValue[this.outputMessagesKey ?? "output"]; } if (typeof newOutputValue === "string") { @@ -116,26 +114,33 @@ export class RunnableWithMessageHistory< } // eslint-disable-next-line @typescript-eslint/no-explicit-any - _enterHistory(input: any, config: CallOptions): Array { + _enterHistory(input: any, config: BaseCallbackConfig): Array { + console.log("Running _enterHistory"); const history = config.configurable?.messageHistory; + // @TODO I think this is broken if (this.historyMessagesKey) { - // todo: this is def not right brace! return history.messages; } - const inputVal = this.inputMessagesKey - ? input[this.inputMessagesKey] - : input; - return [...history.messages, ...this._getInputMessages(inputVal)]; + const inputVal = + input || + (this.inputMessagesKey ? input[this.inputMessagesKey] : undefined); + const historyMessages = history ? history.messages : []; + const returnType = [ + ...historyMessages, + ...this._getInputMessages(inputVal), + ]; + console.log("returning", returnType); + return returnType; } - _exitHistory(run: Run, config: CallOptions): void { + async _exitHistory(run: Run, config: BaseCallbackConfig): Promise { const history = config.configurable?.messageHistory; // Get input messages const { inputs } = run; - const inputValue = inputs[this.inputMessagesKey]; + const inputValue = inputs[this.inputMessagesKey ?? "input"]; const inputMessages = this._getInputMessages(inputValue); // Get output messages const outputValue = run.outputs; @@ -150,17 +155,17 @@ export class RunnableWithMessageHistory< } const outputMessages = this._getOutputMessages(outputValue); - for (const message of [...inputMessages, ...outputMessages]) { - history.addMessage(message); + for await (const message of [...inputMessages, ...outputMessages]) { + await history.addMessage(message); } } - _mergeConfigs(...configs: Array) { + _mergeConfig(...configs: Array) { const config = super._mergeConfig(...configs); // Extract sessionId if (!config.configurable || !config.configurable.sessionId) { const exampleInput = { - [this.inputMessagesKey]: "foo", + [this.inputMessagesKey ?? "input"]: "foo", }; const exampleConfig = { configurable: { sessionId: "123" } }; throw new Error( diff --git a/langchain-core/src/runnables/tests/runnable_history.int.test.ts b/langchain-core/src/runnables/tests/runnable_history.int.test.ts index 70680e574acb..111f9cfcf3fb 100644 --- a/langchain-core/src/runnables/tests/runnable_history.int.test.ts +++ b/langchain-core/src/runnables/tests/runnable_history.int.test.ts @@ -7,12 +7,19 @@ import { FakeChatMessageHistory, } from "../../chat_history.js"; -function getGetSessionHistory(): (sessionId: string) => BaseChatMessageHistory { +async function getGetSessionHistory(): Promise< + (sessionId: string) => Promise +> { const chatHistoryStore: { [key: string]: BaseChatMessageHistory } = {}; - function getSessionHistory(sessionId: string): BaseChatMessageHistory { + async function getSessionHistory( + sessionId: string + ): Promise { if (!(sessionId in chatHistoryStore)) { + console.log("not in store"); chatHistoryStore[sessionId] = new FakeChatMessageHistory(); + } else { + console.log("in store", await chatHistoryStore[sessionId].getMessages()); } return chatHistoryStore[sessionId]; } @@ -22,21 +29,26 @@ function getGetSessionHistory(): (sessionId: string) => BaseChatMessageHistory { test("Runnable with message history", async () => { const runnable = new RunnableLambda({ - func: (messages: HumanMessage[]) => - `you said: ${messages + func: (messages: HumanMessage[]) => { + console.log("runnin", messages); + const messagesArr: HumanMessage[] = !Array.isArray(messages) + ? Object.values(messages) + : messages; + return `you said: ${messagesArr .filter((m) => isBaseMessage(m)) .map((m) => m.content) - .join("\n")}`, + .join("\n")}`; + }, }); - const getSessionHistory = getGetSessionHistory(); + const getMessageHistory = await getGetSessionHistory(); const withHistory = new RunnableWithMessageHistory({ runnable, bound: runnable, kwargs: {}, config: {}, - getMessageHistory: getSessionHistory, + getMessageHistory, }); - const config: RunnableConfig = { configurable: { session_id: "1" } }; + const config: RunnableConfig = { configurable: { sessionId: "1" } }; let output = await withHistory.invoke([new HumanMessage("hello")], config); expect(output).toBe("you said: hello"); output = await withHistory.invoke([new HumanMessage("good bye")], config); From 05b71eae994a819438aa18ee353388628e3b818c Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 12:03:53 -0800 Subject: [PATCH 04/25] adds withListeners method to runnables/callbacks --- langchain-core/src/callbacks/manager.ts | 26 +++- langchain-core/src/runnables/base.ts | 134 +++++++++++++++++--- langchain-core/src/runnables/config.ts | 74 +++++++++++ langchain-core/src/tracers/root_listener.ts | 63 +++++++++ langchain/tsconfig.json | 2 +- 5 files changed, 276 insertions(+), 23 deletions(-) create mode 100644 langchain-core/src/tracers/root_listener.ts diff --git a/langchain-core/src/callbacks/manager.ts b/langchain-core/src/callbacks/manager.ts index 34fe086636a6..1bb20acf30b8 100644 --- a/langchain-core/src/callbacks/manager.ts +++ b/langchain-core/src/callbacks/manager.ts @@ -484,9 +484,9 @@ export class CallbackManager extends BaseCallbackManager implements BaseCallbackManagerMethods { - handlers: BaseCallbackHandler[]; + handlers: BaseCallbackHandler[] = []; - inheritableHandlers: BaseCallbackHandler[]; + inheritableHandlers: BaseCallbackHandler[] = []; tags: string[] = []; @@ -500,10 +500,26 @@ export class CallbackManager private readonly _parentRunId?: string; - constructor(parentRunId?: string) { + constructor( + parentRunId?: string, + options?: { + handlers?: BaseCallbackHandler[]; + inheritableHandlers?: BaseCallbackHandler[]; + tags?: string[]; + inheritableTags?: string[]; + metadata?: Record; + inheritableMetadata?: Record; + } + ) { super(); - this.handlers = []; - this.inheritableHandlers = []; + this.handlers = options?.handlers ?? this.handlers; + this.inheritableHandlers = + options?.inheritableHandlers ?? this.inheritableHandlers; + this.tags = options?.tags ?? this.tags; + this.inheritableTags = options?.inheritableTags ?? this.inheritableTags; + this.metadata = options?.metadata ?? this.metadata; + this.inheritableMetadata = + options?.inheritableMetadata ?? this.inheritableMetadata; this._parentRunId = parentRunId; } diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 0403b91486bb..c8c3d9d91b7c 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -12,8 +12,14 @@ import { } from "../tracers/log_stream.js"; import { Serializable } from "../load/serializable.js"; import { IterableReadableStream } from "../utils/stream.js"; -import { RunnableConfig, getCallbackMangerForConfig } from "./config.js"; +import { + RunnableConfig, + getCallbackMangerForConfig, + mergeConfigs, +} from "./config.js"; import { AsyncCaller } from "../utils/async_caller.js"; +import { Run } from "../tracers/base.js"; +import { RootListenersTracer } from "../tracers/root_listener.js"; export type RunnableFunc = ( input: RunInput @@ -549,6 +555,44 @@ export abstract class Runnable< static isRunnable(thing: any): thing is Runnable { return thing ? thing.lc_runnable : false; } + + /** + * Bind lifecycle listeners to a Runnable, returning a new Runnable. + * The Run object contains information about the run, including its id, + * type, input, output, error, startTime, endTime, and any tags or metadata + * added to the run. + * + * @param {Object} params - The object containing the callback functions. + * @param {(run: Run) => void} params.onStart - Called before the runnable starts running, with the Run object. + * @param {(run: Run) => void} params.onEnd - Called after the runnable finishes running, with the Run object. + * @param {(run: Run) => void} params.onError - Called if the runnable throws an error, with the Run object. + */ + withListeners({ + onStart, + onEnd, + onError, + }: { + onStart?: (run: Run) => void; + onEnd?: (run: Run) => void; + onError?: (run: Run) => void; + }): Runnable { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + return new RunnableBinding({ + bound: this, + config: {}, + configFactories: [ + () => ({ + callbacks: [ + new RootListenersTracer({ + onStart, + onEnd, + onError, + }), + ], + }), + ], + }); + } } export type RunnableBindingArgs< @@ -557,8 +601,9 @@ export type RunnableBindingArgs< CallOptions extends RunnableConfig > = { bound: Runnable; - kwargs: Partial; + kwargs?: Partial; config: RunnableConfig; + configFactories?: Array<() => RunnableConfig>; }; /** @@ -581,31 +626,21 @@ export class RunnableBinding< config: RunnableConfig; - protected kwargs: Partial; + protected kwargs?: Partial; + + configFactories?: Array<() => RunnableConfig>; constructor(fields: RunnableBindingArgs) { super(fields); this.bound = fields.bound; this.kwargs = fields.kwargs; this.config = fields.config; + this.configFactories = fields.configFactories; } // eslint-disable-next-line @typescript-eslint/no-explicit-any _mergeConfig(options?: Record) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const copy: Record = { ...this.config }; - if (options) { - for (const key of Object.keys(options)) { - if (key === "metadata") { - copy[key] = { ...copy[key], ...options[key] }; - } else if (key === "tags") { - copy[key] = (copy[key] ?? []).concat(options[key] ?? []); - } else { - copy[key] = options[key] ?? copy[key]; - } - } - } - return copy as Partial; + return mergeConfigs(this.config, options); } bind( @@ -721,6 +756,45 @@ export class RunnableBinding< ): thing is RunnableBinding { return thing.bound && Runnable.isRunnable(thing.bound); } + + /** + * Bind lifecycle listeners to a Runnable, returning a new Runnable. + * The Run object contains information about the run, including its id, + * type, input, output, error, startTime, endTime, and any tags or metadata + * added to the run. + * + * @param {Object} params - The object containing the callback functions. + * @param {(run: Run) => void} params.onStart - Called before the runnable starts running, with the Run object. + * @param {(run: Run) => void} params.onEnd - Called after the runnable finishes running, with the Run object. + * @param {(run: Run) => void} params.onError - Called if the runnable throws an error, with the Run object. + */ + withListeners({ + onStart, + onEnd, + onError, + }: { + onStart?: (run: Run) => void; + onEnd?: (run: Run) => void; + onError?: (run: Run) => void; + }): Runnable { + // + return new RunnableBinding({ + bound: this.bound, + kwargs: this.kwargs, + config: this.config, + configFactories: [ + () => ({ + callbacks: [ + new RootListenersTracer({ + onStart, + onEnd, + onError, + }), + ], + }), + ], + }); + } } /** @@ -789,6 +863,32 @@ export class RunnableEach< this._patchConfig(config, runManager?.getChild()) ); } + + /** + * Bind lifecycle listeners to a Runnable, returning a new Runnable. + * The Run object contains information about the run, including its id, + * type, input, output, error, startTime, endTime, and any tags or metadata + * added to the run. + * + * @param {Object} params - The object containing the callback functions. + * @param {(run: Run) => void} params.onStart - Called before the runnable starts running, with the Run object. + * @param {(run: Run) => void} params.onEnd - Called after the runnable finishes running, with the Run object. + * @param {(run: Run) => void} params.onError - Called if the runnable throws an error, with the Run object. + */ + withListeners({ + onStart, + onEnd, + onError, + }: { + onStart?: (run: Run) => void; + onEnd?: (run: Run) => void; + onError?: (run: Run) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }): Runnable { + return new RunnableEach({ + bound: this.bound.withListeners({ onStart, onEnd, onError }), + }); + } } /** diff --git a/langchain-core/src/runnables/config.ts b/langchain-core/src/runnables/config.ts index d70cbeb328bc..5473834748b1 100644 --- a/langchain-core/src/runnables/config.ts +++ b/langchain-core/src/runnables/config.ts @@ -14,3 +14,77 @@ export async function getCallbackMangerForConfig(config?: RunnableConfig) { config?.metadata ); } + +export function mergeConfigs( + config: RunnableConfig, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options?: Record +): Partial { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const copy: Record = { ...config }; + const base: RunnableConfig = {}; + if (options) { + for (const key of Object.keys(options)) { + if (key === "metadata") { + copy[key] = { ...copy[key], ...options[key] }; + } else if (key === "tags") { + copy[key] = (copy[key] ?? []).concat(options[key] ?? []); + } else if (key === "callbacks") { + const baseCallbacks = base.callbacks; + const theseCallbacks = options.callbacks ?? config.callbacks; + // callbacks can be either undefined, Array or manager + // so merging two callbacks values has 6 cases + if (Array.isArray(theseCallbacks)) { + if (!baseCallbacks) { + base.callbacks = theseCallbacks; + } else if (Array.isArray(baseCallbacks)) { + base.callbacks = baseCallbacks.concat(theseCallbacks); + } else { + // baseCallbacks is a manager + const manager = baseCallbacks.copy(); + for (const callback of theseCallbacks) { + manager.addHandler(callback, true); + } + base.callbacks = manager; + } + } else if (theseCallbacks) { + // theseCallbacks is a manager + if (!baseCallbacks) { + base.callbacks = theseCallbacks; + } else if (Array.isArray(baseCallbacks)) { + const manager = theseCallbacks.copy(); + for (const callback of baseCallbacks) { + manager.addHandler(callback, true); + } + base.callbacks = manager; + } else { + // baseCallbacks is also a manager + base.callbacks = new CallbackManager(theseCallbacks.parentRunId, { + handlers: baseCallbacks.handlers.concat(theseCallbacks.handlers), + inheritableHandlers: baseCallbacks.inheritableHandlers.concat( + theseCallbacks.inheritableHandlers + ), + tags: Array.from( + new Set(baseCallbacks.tags.concat(theseCallbacks.tags)) + ), + inheritableTags: Array.from( + new Set( + baseCallbacks.inheritableTags.concat( + theseCallbacks.inheritableTags + ) + ) + ), + metadata: { + ...baseCallbacks.metadata, + ...theseCallbacks.metadata, + }, + }); + } + } + } else { + copy[key] = options[key] ?? copy[key]; + } + } + } + return copy as Partial; +} diff --git a/langchain-core/src/tracers/root_listener.ts b/langchain-core/src/tracers/root_listener.ts new file mode 100644 index 000000000000..2c9990bf880c --- /dev/null +++ b/langchain-core/src/tracers/root_listener.ts @@ -0,0 +1,63 @@ +import { BaseTracer, Run } from "./base.js"; + +export class RootListenersTracer extends BaseTracer { + name = "RootListenersTracer"; + + /** The Run's ID. Type UUID */ + rootId?: string; + + argOnStart?: (run: Run) => void; + + argOnEnd?: (run: Run) => void; + + argOnError?: (run: Run) => void; + + constructor({ + onStart, + onEnd, + onError, + }: { + onStart?: (run: Run) => void; + onEnd?: (run: Run) => void; + onError?: (run: Run) => void; + }) { + super(); + this.argOnStart = onStart; + this.argOnEnd = onEnd; + this.argOnError = onError; + } + + /** + * This is a legacy method only called once for an entire run tree + * therefore not useful here + * @param {Run} _ Not used + */ + persistRun(_: Run): Promise { + return Promise.resolve(); + } + + onRunCreate(run: Run) { + if (this.rootId) { + return; + } + + this.rootId = run.id; + + if (this.argOnStart) { + this.argOnStart(run); + } + } + + onRunUpdate(run: Run) { + if (run.id !== this.rootId) { + return; + } + if (!run.error) { + if (this.argOnEnd) { + this.argOnEnd(run); + } + } else if (this.argOnError) { + this.argOnError(run); + } + } +} diff --git a/langchain/tsconfig.json b/langchain/tsconfig.json index ffc49dde54a6..54680cab2458 100644 --- a/langchain/tsconfig.json +++ b/langchain/tsconfig.json @@ -24,7 +24,7 @@ }, "include": [ "src/**/*" - ], +, "../langchain-core/src/tracers/root_listener.ts" ], "exclude": [ "node_modules", "dist", From 2cf91d5811ee059585b9e24ed35c2ae39ec6600a Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 12:23:46 -0800 Subject: [PATCH 05/25] added entrypoint for root listener file --- langchain-core/.gitignore | 3 +++ langchain-core/package.json | 8 ++++++++ langchain-core/scripts/create-entrypoints.js | 1 + langchain-core/src/load/import_map.ts | 1 + 4 files changed, 13 insertions(+) diff --git a/langchain-core/.gitignore b/langchain-core/.gitignore index 62c193054dc8..88d036cfea5a 100644 --- a/langchain-core/.gitignore +++ b/langchain-core/.gitignore @@ -82,6 +82,9 @@ tracers/initialize.d.ts tracers/log_stream.cjs tracers/log_stream.js tracers/log_stream.d.ts +tracers/root_listener.cjs +tracers/root_listener.js +tracers/root_listener.d.ts tracers/run_collector.cjs tracers/run_collector.js tracers/run_collector.d.ts diff --git a/langchain-core/package.json b/langchain-core/package.json index a81b5c30a5c7..e3a5df236610 100644 --- a/langchain-core/package.json +++ b/langchain-core/package.json @@ -219,6 +219,11 @@ "import": "./tracers/log_stream.js", "require": "./tracers/log_stream.cjs" }, + "./tracers/root_listener": { + "types": "./tracers/root_listener.d.ts", + "import": "./tracers/root_listener.js", + "require": "./tracers/root_listener.cjs" + }, "./tracers/run_collector": { "types": "./tracers/run_collector.d.ts", "import": "./tracers/run_collector.js", @@ -367,6 +372,9 @@ "tracers/log_stream.cjs", "tracers/log_stream.js", "tracers/log_stream.d.ts", + "tracers/root_listener.cjs", + "tracers/root_listener.js", + "tracers/root_listener.d.ts", "tracers/run_collector.cjs", "tracers/run_collector.js", "tracers/run_collector.d.ts", diff --git a/langchain-core/scripts/create-entrypoints.js b/langchain-core/scripts/create-entrypoints.js index 31e99061c5b7..728d18c0b2d2 100644 --- a/langchain-core/scripts/create-entrypoints.js +++ b/langchain-core/scripts/create-entrypoints.js @@ -36,6 +36,7 @@ const entrypoints = { "tracers/console": "tracers/console", "tracers/initialize": "tracers/initialize", "tracers/log_stream": "tracers/log_stream", + "tracers/root_listener": "tracers/root_listener", "tracers/run_collector": "tracers/run_collector", "tracers/tracer_langchain_v1": "tracers/tracer_langchain_v1", "tracers/tracer_langchain": "tracers/tracer_langchain", diff --git a/langchain-core/src/load/import_map.ts b/langchain-core/src/load/import_map.ts index 512144832529..d4fd7d3c14b5 100644 --- a/langchain-core/src/load/import_map.ts +++ b/langchain-core/src/load/import_map.ts @@ -27,6 +27,7 @@ export * as tracers__base from "../tracers/base.js"; export * as tracers__console from "../tracers/console.js"; export * as tracers__initialize from "../tracers/initialize.js"; export * as tracers__log_stream from "../tracers/log_stream.js"; +export * as tracers__root_listener from "../tracers/root_listener.js"; export * as tracers__run_collector from "../tracers/run_collector.js"; export * as tracers__tracer_langchain_v1 from "../tracers/tracer_langchain_v1.js"; export * as tracers__tracer_langchain from "../tracers/tracer_langchain.js"; From 21ce187a2876641d2c325553a6884e3bc376811e Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 12:34:39 -0800 Subject: [PATCH 06/25] cr --- langchain-core/.gitignore | 3 --- langchain-core/package.json | 8 -------- langchain-core/scripts/create-entrypoints.js | 1 - langchain-core/src/load/import_map.ts | 1 - langchain/tsconfig.json | 2 +- 5 files changed, 1 insertion(+), 14 deletions(-) diff --git a/langchain-core/.gitignore b/langchain-core/.gitignore index 88d036cfea5a..62c193054dc8 100644 --- a/langchain-core/.gitignore +++ b/langchain-core/.gitignore @@ -82,9 +82,6 @@ tracers/initialize.d.ts tracers/log_stream.cjs tracers/log_stream.js tracers/log_stream.d.ts -tracers/root_listener.cjs -tracers/root_listener.js -tracers/root_listener.d.ts tracers/run_collector.cjs tracers/run_collector.js tracers/run_collector.d.ts diff --git a/langchain-core/package.json b/langchain-core/package.json index e3a5df236610..a81b5c30a5c7 100644 --- a/langchain-core/package.json +++ b/langchain-core/package.json @@ -219,11 +219,6 @@ "import": "./tracers/log_stream.js", "require": "./tracers/log_stream.cjs" }, - "./tracers/root_listener": { - "types": "./tracers/root_listener.d.ts", - "import": "./tracers/root_listener.js", - "require": "./tracers/root_listener.cjs" - }, "./tracers/run_collector": { "types": "./tracers/run_collector.d.ts", "import": "./tracers/run_collector.js", @@ -372,9 +367,6 @@ "tracers/log_stream.cjs", "tracers/log_stream.js", "tracers/log_stream.d.ts", - "tracers/root_listener.cjs", - "tracers/root_listener.js", - "tracers/root_listener.d.ts", "tracers/run_collector.cjs", "tracers/run_collector.js", "tracers/run_collector.d.ts", diff --git a/langchain-core/scripts/create-entrypoints.js b/langchain-core/scripts/create-entrypoints.js index 728d18c0b2d2..31e99061c5b7 100644 --- a/langchain-core/scripts/create-entrypoints.js +++ b/langchain-core/scripts/create-entrypoints.js @@ -36,7 +36,6 @@ const entrypoints = { "tracers/console": "tracers/console", "tracers/initialize": "tracers/initialize", "tracers/log_stream": "tracers/log_stream", - "tracers/root_listener": "tracers/root_listener", "tracers/run_collector": "tracers/run_collector", "tracers/tracer_langchain_v1": "tracers/tracer_langchain_v1", "tracers/tracer_langchain": "tracers/tracer_langchain", diff --git a/langchain-core/src/load/import_map.ts b/langchain-core/src/load/import_map.ts index d4fd7d3c14b5..512144832529 100644 --- a/langchain-core/src/load/import_map.ts +++ b/langchain-core/src/load/import_map.ts @@ -27,7 +27,6 @@ export * as tracers__base from "../tracers/base.js"; export * as tracers__console from "../tracers/console.js"; export * as tracers__initialize from "../tracers/initialize.js"; export * as tracers__log_stream from "../tracers/log_stream.js"; -export * as tracers__root_listener from "../tracers/root_listener.js"; export * as tracers__run_collector from "../tracers/run_collector.js"; export * as tracers__tracer_langchain_v1 from "../tracers/tracer_langchain_v1.js"; export * as tracers__tracer_langchain from "../tracers/tracer_langchain.js"; diff --git a/langchain/tsconfig.json b/langchain/tsconfig.json index 54680cab2458..ffc49dde54a6 100644 --- a/langchain/tsconfig.json +++ b/langchain/tsconfig.json @@ -24,7 +24,7 @@ }, "include": [ "src/**/*" -, "../langchain-core/src/tracers/root_listener.ts" ], + ], "exclude": [ "node_modules", "dist", From a8b2dbd3978899640bd0ac3cef6402b81777a660 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 14:26:45 -0800 Subject: [PATCH 07/25] cr --- langchain-core/src/runnables/config.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/langchain-core/src/runnables/config.ts b/langchain-core/src/runnables/config.ts index 5473834748b1..6491d38ac25f 100644 --- a/langchain-core/src/runnables/config.ts +++ b/langchain-core/src/runnables/config.ts @@ -22,7 +22,6 @@ export function mergeConfigs( ): Partial { // eslint-disable-next-line @typescript-eslint/no-explicit-any const copy: Record = { ...config }; - const base: RunnableConfig = {}; if (options) { for (const key of Object.keys(options)) { if (key === "metadata") { @@ -30,36 +29,36 @@ export function mergeConfigs( } else if (key === "tags") { copy[key] = (copy[key] ?? []).concat(options[key] ?? []); } else if (key === "callbacks") { - const baseCallbacks = base.callbacks; + const baseCallbacks = copy.callbacks; const theseCallbacks = options.callbacks ?? config.callbacks; // callbacks can be either undefined, Array or manager // so merging two callbacks values has 6 cases if (Array.isArray(theseCallbacks)) { if (!baseCallbacks) { - base.callbacks = theseCallbacks; + copy.callbacks = theseCallbacks; } else if (Array.isArray(baseCallbacks)) { - base.callbacks = baseCallbacks.concat(theseCallbacks); + copy.callbacks = baseCallbacks.concat(theseCallbacks); } else { // baseCallbacks is a manager const manager = baseCallbacks.copy(); for (const callback of theseCallbacks) { manager.addHandler(callback, true); } - base.callbacks = manager; + copy.callbacks = manager; } } else if (theseCallbacks) { // theseCallbacks is a manager if (!baseCallbacks) { - base.callbacks = theseCallbacks; + copy.callbacks = theseCallbacks; } else if (Array.isArray(baseCallbacks)) { const manager = theseCallbacks.copy(); for (const callback of baseCallbacks) { manager.addHandler(callback, true); } - base.callbacks = manager; + copy.callbacks = manager; } else { // baseCallbacks is also a manager - base.callbacks = new CallbackManager(theseCallbacks.parentRunId, { + copy.callbacks = new CallbackManager(theseCallbacks.parentRunId, { handlers: baseCallbacks.handlers.concat(theseCallbacks.handlers), inheritableHandlers: baseCallbacks.inheritableHandlers.concat( theseCallbacks.inheritableHandlers From 48e1f7e0049c700b53c2e102cad638d82ef5a37f Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 15:00:44 -0800 Subject: [PATCH 08/25] cr --- langchain-core/src/runnables/base.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index c8c3d9d91b7c..7beef6aa56d0 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -576,6 +576,7 @@ export abstract class Runnable< onEnd?: (run: Run) => void; onError?: (run: Run) => void; }): Runnable { + console.log("gets called"); // eslint-disable-next-line @typescript-eslint/no-use-before-define return new RunnableBinding({ bound: this, @@ -603,7 +604,7 @@ export type RunnableBindingArgs< bound: Runnable; kwargs?: Partial; config: RunnableConfig; - configFactories?: Array<() => RunnableConfig>; + configFactories?: Array<(config: RunnableConfig) => RunnableConfig>; }; /** @@ -628,7 +629,7 @@ export class RunnableBinding< protected kwargs?: Partial; - configFactories?: Array<() => RunnableConfig>; + configFactories?: Array<(config: RunnableConfig) => RunnableConfig>; constructor(fields: RunnableBindingArgs) { super(fields); @@ -639,8 +640,15 @@ export class RunnableBinding< } // eslint-disable-next-line @typescript-eslint/no-explicit-any - _mergeConfig(options?: Record) { - return mergeConfigs(this.config, options); + _mergeConfig(options?: Record): Partial { + const config = mergeConfigs(this.config, options); + return mergeConfigs( + config, + ...(this.configFactories + ? this.configFactories.map((f) => f(config)) + : []) + ); + // return mergeConfigs(this.config, options); } bind( From a56cd19142ac00348448b6b8646205c22f1f6e18 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 15:01:01 -0800 Subject: [PATCH 09/25] cr --- langchain-core/src/runnables/base.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 7beef6aa56d0..2d3bc6a95f53 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -576,7 +576,6 @@ export abstract class Runnable< onEnd?: (run: Run) => void; onError?: (run: Run) => void; }): Runnable { - console.log("gets called"); // eslint-disable-next-line @typescript-eslint/no-use-before-define return new RunnableBinding({ bound: this, @@ -648,7 +647,6 @@ export class RunnableBinding< ? this.configFactories.map((f) => f(config)) : []) ); - // return mergeConfigs(this.config, options); } bind( From 89d689d1b2089406b4beb5433c2848837e91fedc Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 15:01:18 -0800 Subject: [PATCH 10/25] cr --- langchain/src/schema/tests/runnable.test.ts | 31 ++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/langchain/src/schema/tests/runnable.test.ts b/langchain/src/schema/tests/runnable.test.ts index ffd78a2c50c7..4093814050d0 100644 --- a/langchain/src/schema/tests/runnable.test.ts +++ b/langchain/src/schema/tests/runnable.test.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { z } from "zod"; -import { test } from "@jest/globals"; +import { jest, test } from "@jest/globals"; import { createChatMessageChunkEncoderStream } from "../../chat_models/base.js"; import { ChatPromptTemplate, @@ -28,6 +28,7 @@ import { FakeStreamingLLM, FakeSplitIntoListParser, } from "./lib.js"; +import { FakeListChatModel } from "../../chat_models/fake.js"; test("Test batch", async () => { const llm = new FakeLLM({}); @@ -247,3 +248,31 @@ test("Runnable withConfig", async () => { expect(chunks[0]?.tags).toEqual(["a-tag", "b-tag"]); expect(chunks[0]?.metadata).toEqual({ a: "updated", b: "c" }); }); + +test("Listeners work", async () => { + // implement + const prompt = ChatPromptTemplate.fromMessages([ + SystemMessagePromptTemplate.fromTemplate("You are a nice assistant."), + ["human", "{question}"], + ]); + const model = new FakeListChatModel({ + responses: ["foo"], + }); + const chain = prompt.pipe(model); + + const mockStart = jest.fn(); + const mockEnd = jest.fn(); + + await chain + .withListeners({ + onStart: mockStart, + onEnd: mockEnd, + }) + .invoke({ question: "What is the meaning of life?" }); + + expect(mockStart).toHaveBeenCalledTimes(1); + expect((mockStart.mock.calls[0][0] as { name: string }).name).toBe( + "RunnableSequence" + ); + expect(mockEnd).toHaveBeenCalledTimes(1); +}); From be79ae6d056e0c46d139cd2694cde6413f60c982 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 15:20:14 -0800 Subject: [PATCH 11/25] support async listeners --- langchain-core/src/runnables/base.ts | 53 ++++++++++++--------- langchain-core/src/tracers/root_listener.ts | 22 ++++----- langchain/src/schema/tests/runnable.test.ts | 46 ++++++++++++++++-- 3 files changed, 85 insertions(+), 36 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 2d3bc6a95f53..1592bda8df13 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -572,9 +572,9 @@ export abstract class Runnable< onEnd, onError, }: { - onStart?: (run: Run) => void; - onEnd?: (run: Run) => void; - onError?: (run: Run) => void; + onStart?: (run: Run) => void | Promise; + onEnd?: (run: Run) => void | Promise; + onError?: (run: Run) => void | Promise; }): Runnable { // eslint-disable-next-line @typescript-eslint/no-use-before-define return new RunnableBinding({ @@ -628,7 +628,9 @@ export class RunnableBinding< protected kwargs?: Partial; - configFactories?: Array<(config: RunnableConfig) => RunnableConfig>; + configFactories?: Array< + (config: RunnableConfig) => RunnableConfig | Promise + >; constructor(fields: RunnableBindingArgs) { super(fields); @@ -639,12 +641,16 @@ export class RunnableBinding< } // eslint-disable-next-line @typescript-eslint/no-explicit-any - _mergeConfig(options?: Record): Partial { + async _mergeConfig( + options?: Record + ): Promise> { const config = mergeConfigs(this.config, options); return mergeConfigs( config, ...(this.configFactories - ? this.configFactories.map((f) => f(config)) + ? await Promise.all( + this.configFactories.map(async (f) => await f(config)) + ) : []) ); } @@ -686,7 +692,7 @@ export class RunnableBinding< ): Promise { return this.bound.invoke( input, - this._mergeConfig({ ...options, ...this.kwargs }) + await this._mergeConfig({ ...options, ...this.kwargs }) ); } @@ -714,13 +720,16 @@ export class RunnableBinding< batchOptions?: RunnableBatchOptions ): Promise<(RunOutput | Error)[]> { const mergedOptions = Array.isArray(options) - ? options.map((individualOption) => - this._mergeConfig({ - ...individualOption, - ...this.kwargs, - }) + ? await Promise.all( + options.map( + async (individualOption) => + await this._mergeConfig({ + ...individualOption, + ...this.kwargs, + }) + ) ) - : this._mergeConfig({ ...options, ...this.kwargs }); + : await this._mergeConfig({ ...options, ...this.kwargs }); return this.bound.batch(inputs, mergedOptions, batchOptions); } @@ -730,7 +739,7 @@ export class RunnableBinding< ) { yield* this.bound._streamIterator( input, - this._mergeConfig({ ...options, ...this.kwargs }) + await this._mergeConfig({ ...options, ...this.kwargs }) ); } @@ -740,7 +749,7 @@ export class RunnableBinding< ): Promise> { return this.bound.stream( input, - this._mergeConfig({ ...options, ...this.kwargs }) + await this._mergeConfig({ ...options, ...this.kwargs }) ); } @@ -751,7 +760,7 @@ export class RunnableBinding< ): AsyncGenerator { yield* this.bound.transform( generator, - this._mergeConfig({ ...options, ...this.kwargs }) + await this._mergeConfig({ ...options, ...this.kwargs }) ); } @@ -779,9 +788,9 @@ export class RunnableBinding< onEnd, onError, }: { - onStart?: (run: Run) => void; - onEnd?: (run: Run) => void; - onError?: (run: Run) => void; + onStart?: (run: Run) => void | Promise; + onEnd?: (run: Run) => void | Promise; + onError?: (run: Run) => void | Promise; }): Runnable { // return new RunnableBinding({ @@ -886,9 +895,9 @@ export class RunnableEach< onEnd, onError, }: { - onStart?: (run: Run) => void; - onEnd?: (run: Run) => void; - onError?: (run: Run) => void; + onStart?: (run: Run) => void | Promise; + onEnd?: (run: Run) => void | Promise; + onError?: (run: Run) => void | Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any }): Runnable { return new RunnableEach({ diff --git a/langchain-core/src/tracers/root_listener.ts b/langchain-core/src/tracers/root_listener.ts index 2c9990bf880c..cabb4a9dba74 100644 --- a/langchain-core/src/tracers/root_listener.ts +++ b/langchain-core/src/tracers/root_listener.ts @@ -6,20 +6,20 @@ export class RootListenersTracer extends BaseTracer { /** The Run's ID. Type UUID */ rootId?: string; - argOnStart?: (run: Run) => void; + argOnStart?: (run: Run) => void | Promise; - argOnEnd?: (run: Run) => void; + argOnEnd?: (run: Run) => void | Promise; - argOnError?: (run: Run) => void; + argOnError?: (run: Run) => void | Promise; constructor({ onStart, onEnd, onError, }: { - onStart?: (run: Run) => void; - onEnd?: (run: Run) => void; - onError?: (run: Run) => void; + onStart?: (run: Run) => void | Promise; + onEnd?: (run: Run) => void | Promise; + onError?: (run: Run) => void | Promise; }) { super(); this.argOnStart = onStart; @@ -36,7 +36,7 @@ export class RootListenersTracer extends BaseTracer { return Promise.resolve(); } - onRunCreate(run: Run) { + async onRunCreate(run: Run) { if (this.rootId) { return; } @@ -44,20 +44,20 @@ export class RootListenersTracer extends BaseTracer { this.rootId = run.id; if (this.argOnStart) { - this.argOnStart(run); + await this.argOnStart(run); } } - onRunUpdate(run: Run) { + async onRunUpdate(run: Run) { if (run.id !== this.rootId) { return; } if (!run.error) { if (this.argOnEnd) { - this.argOnEnd(run); + await this.argOnEnd(run); } } else if (this.argOnError) { - this.argOnError(run); + await this.argOnError(run); } } } diff --git a/langchain/src/schema/tests/runnable.test.ts b/langchain/src/schema/tests/runnable.test.ts index 4093814050d0..223e5710ce0e 100644 --- a/langchain/src/schema/tests/runnable.test.ts +++ b/langchain/src/schema/tests/runnable.test.ts @@ -250,7 +250,6 @@ test("Runnable withConfig", async () => { }); test("Listeners work", async () => { - // implement const prompt = ChatPromptTemplate.fromMessages([ SystemMessagePromptTemplate.fromTemplate("You are a nice assistant."), ["human", "{question}"], @@ -265,8 +264,49 @@ test("Listeners work", async () => { await chain .withListeners({ - onStart: mockStart, - onEnd: mockEnd, + onStart: (run) => { + mockStart(run); + }, + onEnd: (run) => { + mockEnd(run); + }, + }) + .invoke({ question: "What is the meaning of life?" }); + + expect(mockStart).toHaveBeenCalledTimes(1); + expect((mockStart.mock.calls[0][0] as { name: string }).name).toBe( + "RunnableSequence" + ); + expect(mockEnd).toHaveBeenCalledTimes(1); +}); + +test("Listeners work with async handlers", async () => { + const prompt = ChatPromptTemplate.fromMessages([ + SystemMessagePromptTemplate.fromTemplate("You are a nice assistant."), + ["human", "{question}"], + ]); + const model = new FakeListChatModel({ + responses: ["foo"], + }); + const chain = prompt.pipe(model); + + const mockStart = jest.fn(); + const mockEnd = jest.fn(); + + await chain + .withListeners({ + // eslint-disable-next-line @typescript-eslint/no-misused-promises + onStart: async (run) => { + const promise = new Promise((resolve) => setTimeout(resolve, 2000)); + await promise; + mockStart(run); + }, + // eslint-disable-next-line @typescript-eslint/no-misused-promises + onEnd: async (run) => { + const promise = new Promise((resolve) => setTimeout(resolve, 2000)); + await promise; + mockEnd(run); + }, }) .invoke({ question: "What is the meaning of life?" }); From b71e30da8ff0cd57e8f59fb586def3a884db0c1d Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 15:46:55 -0800 Subject: [PATCH 12/25] allow for run or run and config as args to listener funcs --- langchain-core/src/runnables/base.ts | 51 ++++++++++++++----- langchain-core/src/tracers/root_listener.ts | 54 +++++++++++++++++---- 2 files changed, 85 insertions(+), 20 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 1592bda8df13..2f02c0543025 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -572,18 +572,28 @@ export abstract class Runnable< onEnd, onError, }: { - onStart?: (run: Run) => void | Promise; - onEnd?: (run: Run) => void | Promise; - onError?: (run: Run) => void | Promise; + onStart?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onEnd?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onError?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; }): Runnable { // eslint-disable-next-line @typescript-eslint/no-use-before-define return new RunnableBinding({ bound: this, config: {}, configFactories: [ - () => ({ + (config) => ({ callbacks: [ new RootListenersTracer({ + config, onStart, onEnd, onError, @@ -788,9 +798,18 @@ export class RunnableBinding< onEnd, onError, }: { - onStart?: (run: Run) => void | Promise; - onEnd?: (run: Run) => void | Promise; - onError?: (run: Run) => void | Promise; + onStart?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onEnd?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onError?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; }): Runnable { // return new RunnableBinding({ @@ -798,9 +817,10 @@ export class RunnableBinding< kwargs: this.kwargs, config: this.config, configFactories: [ - () => ({ + (config) => ({ callbacks: [ new RootListenersTracer({ + config, onStart, onEnd, onError, @@ -895,9 +915,18 @@ export class RunnableEach< onEnd, onError, }: { - onStart?: (run: Run) => void | Promise; - onEnd?: (run: Run) => void | Promise; - onError?: (run: Run) => void | Promise; + onStart?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onEnd?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onError?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; // eslint-disable-next-line @typescript-eslint/no-explicit-any }): Runnable { return new RunnableEach({ diff --git a/langchain-core/src/tracers/root_listener.ts b/langchain-core/src/tracers/root_listener.ts index cabb4a9dba74..486bd59e6b07 100644 --- a/langchain-core/src/tracers/root_listener.ts +++ b/langchain-core/src/tracers/root_listener.ts @@ -1,3 +1,4 @@ +import { RunnableConfig } from "../runnables/config.js"; import { BaseTracer, Run } from "./base.js"; export class RootListenersTracer extends BaseTracer { @@ -6,22 +7,45 @@ export class RootListenersTracer extends BaseTracer { /** The Run's ID. Type UUID */ rootId?: string; - argOnStart?: (run: Run) => void | Promise; + config: RunnableConfig; - argOnEnd?: (run: Run) => void | Promise; + argOnStart?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; - argOnError?: (run: Run) => void | Promise; + argOnEnd?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + + argOnError?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; constructor({ + config, onStart, onEnd, onError, }: { - onStart?: (run: Run) => void | Promise; - onEnd?: (run: Run) => void | Promise; - onError?: (run: Run) => void | Promise; + config: RunnableConfig; + onStart?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onEnd?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; + onError?: { + (run: Run): void | Promise; + (run: Run, config: RunnableConfig): void | Promise; + }; }) { super(); + this.config = config; this.argOnStart = onStart; this.argOnEnd = onEnd; this.argOnError = onError; @@ -44,7 +68,11 @@ export class RootListenersTracer extends BaseTracer { this.rootId = run.id; if (this.argOnStart) { - await this.argOnStart(run); + if (this.argOnStart.length === 1) { + await this.argOnStart(run); + } else if (this.argOnStart.length === 2) { + await this.argOnStart(run, this.config); + } } } @@ -54,10 +82,18 @@ export class RootListenersTracer extends BaseTracer { } if (!run.error) { if (this.argOnEnd) { - await this.argOnEnd(run); + if (this.argOnEnd.length === 1) { + await this.argOnEnd(run); + } else if (this.argOnEnd.length === 2) { + await this.argOnEnd(run, this.config); + } } } else if (this.argOnError) { - await this.argOnError(run); + if (this.argOnError.length === 1) { + await this.argOnError(run); + } else if (this.argOnError.length === 2) { + await this.argOnError(run, this.config); + } } } } From e8432ac578a79504a66f38e8f73b91c444b7cd32 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 17:02:02 -0800 Subject: [PATCH 13/25] cr --- langchain-core/src/runnables/history.ts | 47 ++++++++++--------- .../tests/runnable_history.int.test.ts | 17 ++----- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index c88a2f1f35e2..39222e144558 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -35,7 +35,10 @@ export class RunnableWithMessageHistory< getMessageHistory: GetSessionHistoryCallable; constructor( - fields: RunnableBindingArgs & { + fields: Omit< + RunnableBindingArgs, + "bound" + > & { runnable: Runnable; getMessageHistory: GetSessionHistoryCallable; inputMessagesKey?: string; @@ -43,19 +46,9 @@ export class RunnableWithMessageHistory< historyMessagesKey?: string; } ) { - super(fields); - this.runnable = fields.runnable; - this.getMessageHistory = fields.getMessageHistory; - this.inputMessagesKey = fields.inputMessagesKey; - this.outputMessagesKey = fields.outputMessagesKey; - this.historyMessagesKey = fields.historyMessagesKey; - let historyChain: Runnable = new RunnableLambda({ // eslint-disable-next-line @typescript-eslint/no-explicit-any - func: (input: any) => { - console.log("HISTORY CHAIN CALLED", input); - return this._enterHistory(input, {}); - }, + func: (input: any) => this._enterHistory(input, {}), }).withConfig({ runName: "loadHistory" }); const messagesKey = fields.historyMessagesKey || fields.inputMessagesKey; @@ -66,10 +59,23 @@ export class RunnableWithMessageHistory< } const bound = historyChain - .pipe(fields.runnable) + .pipe( + fields.runnable.withListeners({ + // eslint-disable-next-line @typescript-eslint/no-misused-promises + onEnd: (run, config) => this._exitHistory(run, config), + }) + ) .withConfig({ runName: "RunnableWithMessageHistory" }); - this.bound = bound; + super({ + ...fields, + bound, + }); + this.runnable = fields.runnable; + this.getMessageHistory = fields.getMessageHistory; + this.inputMessagesKey = fields.inputMessagesKey; + this.outputMessagesKey = fields.outputMessagesKey; + this.historyMessagesKey = fields.historyMessagesKey; } _getInputMessages( @@ -115,10 +121,8 @@ export class RunnableWithMessageHistory< // eslint-disable-next-line @typescript-eslint/no-explicit-any _enterHistory(input: any, config: BaseCallbackConfig): Array { - console.log("Running _enterHistory"); - const history = config.configurable?.messageHistory; + const history = config?.configurable?.messageHistory; - // @TODO I think this is broken if (this.historyMessagesKey) { return history.messages; } @@ -131,7 +135,6 @@ export class RunnableWithMessageHistory< ...historyMessages, ...this._getInputMessages(inputVal), ]; - console.log("returning", returnType); return returnType; } @@ -160,8 +163,8 @@ export class RunnableWithMessageHistory< } } - _mergeConfig(...configs: Array) { - const config = super._mergeConfig(...configs); + async _mergeConfig(...configs: Array) { + const config = await super._mergeConfig(...configs); // Extract sessionId if (!config.configurable || !config.configurable.sessionId) { const exampleInput = { @@ -177,7 +180,9 @@ export class RunnableWithMessageHistory< } // attach messageHistory const { sessionId } = config.configurable; - config.configurable.messageHistory = this.getMessageHistory(sessionId); + config.configurable.messageHistory = await this.getMessageHistory( + sessionId + ); return config; } } diff --git a/langchain-core/src/runnables/tests/runnable_history.int.test.ts b/langchain-core/src/runnables/tests/runnable_history.int.test.ts index 111f9cfcf3fb..bb7da5e47847 100644 --- a/langchain-core/src/runnables/tests/runnable_history.int.test.ts +++ b/langchain-core/src/runnables/tests/runnable_history.int.test.ts @@ -16,10 +16,7 @@ async function getGetSessionHistory(): Promise< sessionId: string ): Promise { if (!(sessionId in chatHistoryStore)) { - console.log("not in store"); chatHistoryStore[sessionId] = new FakeChatMessageHistory(); - } else { - console.log("in store", await chatHistoryStore[sessionId].getMessages()); } return chatHistoryStore[sessionId]; } @@ -29,22 +26,16 @@ async function getGetSessionHistory(): Promise< test("Runnable with message history", async () => { const runnable = new RunnableLambda({ - func: (messages: HumanMessage[]) => { - console.log("runnin", messages); - const messagesArr: HumanMessage[] = !Array.isArray(messages) - ? Object.values(messages) - : messages; - return `you said: ${messagesArr + func: (messages: HumanMessage[]) => + `you said: ${messages .filter((m) => isBaseMessage(m)) .map((m) => m.content) - .join("\n")}`; - }, + .join("\n")}`, }); + const getMessageHistory = await getGetSessionHistory(); const withHistory = new RunnableWithMessageHistory({ runnable, - bound: runnable, - kwargs: {}, config: {}, getMessageHistory, }); From 00ab504b2c74cf8c426134f85055a37c82026aa1 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Wed, 29 Nov 2023 17:03:26 -0800 Subject: [PATCH 14/25] chore: lint files --- .../src/runnables/tests/runnable_history.int.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/langchain-core/src/runnables/tests/runnable_history.int.test.ts b/langchain-core/src/runnables/tests/runnable_history.int.test.ts index bb7da5e47847..cceba6e73ea8 100644 --- a/langchain-core/src/runnables/tests/runnable_history.int.test.ts +++ b/langchain-core/src/runnables/tests/runnable_history.int.test.ts @@ -6,7 +6,10 @@ import { BaseChatMessageHistory, FakeChatMessageHistory, } from "../../chat_history.js"; - +/** + * @TODO Move this to `langchain` and replace real llm with fake. + * Only reason it's here is so I don't have to rebuild every time I make a change. + */ async function getGetSessionHistory(): Promise< (sessionId: string) => Promise > { From 08698aa33511d0c1a32cb587cec5b98d4e8dc086 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Thu, 30 Nov 2023 11:10:40 -0800 Subject: [PATCH 15/25] cr --- langchain-core/src/runnables/base.ts | 5 +++-- langchain-core/src/runnables/history.ts | 5 +++-- ...e_history.int.test.ts => runnable_history.test.ts} | 11 ++++------- 3 files changed, 10 insertions(+), 11 deletions(-) rename langchain-core/src/runnables/tests/{runnable_history.int.test.ts => runnable_history.test.ts} (81%) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 2f02c0543025..7c3ec1c02850 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -22,7 +22,8 @@ import { Run } from "../tracers/base.js"; import { RootListenersTracer } from "../tracers/root_listener.js"; export type RunnableFunc = ( - input: RunInput + input: RunInput, + options?: Record ) => RunOutput | Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1526,7 +1527,7 @@ export class RunnableLambda extends Runnable< config?: Partial, runManager?: CallbackManagerForChainRun ) { - let output = await this.func(input); + let output = await this.func(input, config); if (output && Runnable.isRunnable(output)) { output = await output.invoke( input, diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index 39222e144558..fe5dc945f9b5 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -48,7 +48,8 @@ export class RunnableWithMessageHistory< ) { let historyChain: Runnable = new RunnableLambda({ // eslint-disable-next-line @typescript-eslint/no-explicit-any - func: (input: any) => this._enterHistory(input, {}), + func: (input: any, options?: Record) => + this._enterHistory(input, options ?? {}), }).withConfig({ runName: "loadHistory" }); const messagesKey = fields.historyMessagesKey || fields.inputMessagesKey; @@ -172,7 +173,7 @@ export class RunnableWithMessageHistory< }; const exampleConfig = { configurable: { sessionId: "123" } }; throw new Error( - `session_id is required. Pass it in as part of the config argument to .invoke() or .stream()\n` + + `sessionId is required. Pass it in as part of the config argument to .invoke() or .stream()\n` + `eg. chain.invoke(${JSON.stringify(exampleInput)}, ${JSON.stringify( exampleConfig )})` diff --git a/langchain-core/src/runnables/tests/runnable_history.int.test.ts b/langchain-core/src/runnables/tests/runnable_history.test.ts similarity index 81% rename from langchain-core/src/runnables/tests/runnable_history.int.test.ts rename to langchain-core/src/runnables/tests/runnable_history.test.ts index cceba6e73ea8..c6e5d1822e63 100644 --- a/langchain-core/src/runnables/tests/runnable_history.int.test.ts +++ b/langchain-core/src/runnables/tests/runnable_history.test.ts @@ -1,4 +1,4 @@ -import { HumanMessage, isBaseMessage } from "../../messages/index.js"; +import { BaseMessage, HumanMessage } from "../../messages/index.js"; import { RunnableLambda } from "../base.js"; import { RunnableConfig } from "../config.js"; import { RunnableWithMessageHistory } from "../history.js"; @@ -6,10 +6,7 @@ import { BaseChatMessageHistory, FakeChatMessageHistory, } from "../../chat_history.js"; -/** - * @TODO Move this to `langchain` and replace real llm with fake. - * Only reason it's here is so I don't have to rebuild every time I make a change. - */ + async function getGetSessionHistory(): Promise< (sessionId: string) => Promise > { @@ -29,9 +26,9 @@ async function getGetSessionHistory(): Promise< test("Runnable with message history", async () => { const runnable = new RunnableLambda({ - func: (messages: HumanMessage[]) => + func: (messages: BaseMessage[]) => `you said: ${messages - .filter((m) => isBaseMessage(m)) + .filter((m) => m._getType() === "human") .map((m) => m.content) .join("\n")}`, }); From b05533729d5b93ce0be7598ec3108e7430c0213e Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Thu, 30 Nov 2023 11:16:19 -0800 Subject: [PATCH 16/25] cr --- langchain-core/src/runnables/history.ts | 6 +++--- langchain/src/schema/tests/runnable.test.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index fe5dc945f9b5..178549ac66c5 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -13,6 +13,7 @@ import { RunnableBindingArgs, RunnableLambda, } from "./base.js"; +import { RunnableConfig } from "./config.js"; import { RunnablePassthrough } from "./passthrough.js"; type GetSessionHistoryCallable = ( @@ -47,7 +48,6 @@ export class RunnableWithMessageHistory< } ) { let historyChain: Runnable = new RunnableLambda({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any func: (input: any, options?: Record) => this._enterHistory(input, options ?? {}), }).withConfig({ runName: "loadHistory" }); @@ -62,8 +62,8 @@ export class RunnableWithMessageHistory< const bound = historyChain .pipe( fields.runnable.withListeners({ - // eslint-disable-next-line @typescript-eslint/no-misused-promises - onEnd: (run, config) => this._exitHistory(run, config), + onEnd: (run: Run, config: RunnableConfig = {}) => + this._exitHistory(run, config), }) ) .withConfig({ runName: "RunnableWithMessageHistory" }); diff --git a/langchain/src/schema/tests/runnable.test.ts b/langchain/src/schema/tests/runnable.test.ts index 223e5710ce0e..8011d2e922ba 100644 --- a/langchain/src/schema/tests/runnable.test.ts +++ b/langchain/src/schema/tests/runnable.test.ts @@ -29,6 +29,7 @@ import { FakeSplitIntoListParser, } from "./lib.js"; import { FakeListChatModel } from "../../chat_models/fake.js"; +import { Run } from "../../callbacks/index.js"; test("Test batch", async () => { const llm = new FakeLLM({}); @@ -264,10 +265,10 @@ test("Listeners work", async () => { await chain .withListeners({ - onStart: (run) => { + onStart: (run: Run) => { mockStart(run); }, - onEnd: (run) => { + onEnd: (run: Run) => { mockEnd(run); }, }) @@ -295,14 +296,13 @@ test("Listeners work with async handlers", async () => { await chain .withListeners({ - // eslint-disable-next-line @typescript-eslint/no-misused-promises - onStart: async (run) => { + onStart: async (run: Run) => { const promise = new Promise((resolve) => setTimeout(resolve, 2000)); await promise; mockStart(run); }, // eslint-disable-next-line @typescript-eslint/no-misused-promises - onEnd: async (run) => { + onEnd: async (run: Run) => { const promise = new Promise((resolve) => setTimeout(resolve, 2000)); await promise; mockEnd(run); From 8e23d5db3dfc155f66970001ad28a9be0e6e0f6f Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Thu, 30 Nov 2023 15:11:45 -0800 Subject: [PATCH 17/25] eslint disbale any --- langchain-core/src/runnables/base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 2f02c0543025..5387a19bef24 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -650,8 +650,8 @@ export class RunnableBinding< this.configFactories = fields.configFactories; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any async _mergeConfig( + // eslint-disable-next-line @typescript-eslint/no-explicit-any options?: Record ): Promise> { const config = mergeConfigs(this.config, options); From 670f4f1308d831301b6c9f1be5201bca9399b67e Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 1 Dec 2023 09:10:23 -0800 Subject: [PATCH 18/25] update types --- langchain-core/src/runnables/base.ts | 45 +++++---------------- langchain-core/src/tracers/root_listener.ts | 15 ++----- 2 files changed, 12 insertions(+), 48 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 5387a19bef24..cffccc316e8e 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -572,18 +572,9 @@ export abstract class Runnable< onEnd, onError, }: { - onStart?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onEnd?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onError?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; + onStart?: (run: Run, config?: RunnableConfig) => void | Promise; + onEnd?: (run: Run, config?: RunnableConfig) => void | Promise; + onError?: (run: Run, config?: RunnableConfig) => void | Promise; }): Runnable { // eslint-disable-next-line @typescript-eslint/no-use-before-define return new RunnableBinding({ @@ -798,18 +789,9 @@ export class RunnableBinding< onEnd, onError, }: { - onStart?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onEnd?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onError?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; + onStart?: (run: Run, config?: RunnableConfig) => void | Promise; + onEnd?: (run: Run, config?: RunnableConfig) => void | Promise; + onError?: (run: Run, config?: RunnableConfig) => void | Promise; }): Runnable { // return new RunnableBinding({ @@ -915,18 +897,9 @@ export class RunnableEach< onEnd, onError, }: { - onStart?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onEnd?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onError?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; + onStart?: (run: Run, config?: RunnableConfig) => void | Promise; + onEnd?: (run: Run, config?: RunnableConfig) => void | Promise; + onError?: (run: Run, config?: RunnableConfig) => void | Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any }): Runnable { return new RunnableEach({ diff --git a/langchain-core/src/tracers/root_listener.ts b/langchain-core/src/tracers/root_listener.ts index 486bd59e6b07..222c307f531f 100644 --- a/langchain-core/src/tracers/root_listener.ts +++ b/langchain-core/src/tracers/root_listener.ts @@ -31,18 +31,9 @@ export class RootListenersTracer extends BaseTracer { onError, }: { config: RunnableConfig; - onStart?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onEnd?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; - onError?: { - (run: Run): void | Promise; - (run: Run, config: RunnableConfig): void | Promise; - }; + onStart?: (run: Run, config?: RunnableConfig) => void | Promise; + onEnd?: (run: Run, config?: RunnableConfig) => void | Promise; + onError?: (run: Run, config?: RunnableConfig) => void | Promise; }) { super(); this.config = config; From cec10acf9b14f0b32e3de50f322ea792e32b0e1e Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 1 Dec 2023 10:03:55 -0800 Subject: [PATCH 19/25] cr --- examples/src/chains/retrieval_qa.ts | 2 +- examples/src/guides/expression_language/cookbook_retriever.ts | 2 +- examples/src/models/chat/integration_llama_cpp_stream_multi.ts | 2 +- langchain-core/src/runnables/base.ts | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/src/chains/retrieval_qa.ts b/examples/src/chains/retrieval_qa.ts index 2e8706b1f4fd..803ea724a6b2 100644 --- a/examples/src/chains/retrieval_qa.ts +++ b/examples/src/chains/retrieval_qa.ts @@ -39,7 +39,7 @@ const prompt = ChatPromptTemplate.fromMessages(messages); const chain = RunnableSequence.from([ { - context: vectorStoreRetriever.pipe(formatDocumentsAsString), + context: vectorStoreRetriever.pipe((docs) => formatDocumentsAsString(docs)), question: new RunnablePassthrough(), }, prompt, diff --git a/examples/src/guides/expression_language/cookbook_retriever.ts b/examples/src/guides/expression_language/cookbook_retriever.ts index 800e0d810c41..c2f33280702c 100644 --- a/examples/src/guides/expression_language/cookbook_retriever.ts +++ b/examples/src/guides/expression_language/cookbook_retriever.ts @@ -26,7 +26,7 @@ Question: {question}`); const chain = RunnableSequence.from([ { - context: retriever.pipe(formatDocumentsAsString), + context: retriever.pipe((docs) => formatDocumentsAsString(docs)), question: new RunnablePassthrough(), }, prompt, diff --git a/examples/src/models/chat/integration_llama_cpp_stream_multi.ts b/examples/src/models/chat/integration_llama_cpp_stream_multi.ts index 6277ac69b52b..35633c6b83fe 100644 --- a/examples/src/models/chat/integration_llama_cpp_stream_multi.ts +++ b/examples/src/models/chat/integration_llama_cpp_stream_multi.ts @@ -3,7 +3,7 @@ import { SystemMessage, HumanMessage } from "langchain/schema"; const llamaPath = "/Replace/with/path/to/your/model/gguf-llama2-q4_0.bin"; -const model = new ChatLlamaCpp({ modelPath: llamaPath, temperature: 0.7 }); +const llamaCpp = new ChatLlamaCpp({ modelPath: llamaPath, temperature: 0.7 }); const stream = await llamaCpp.stream([ new SystemMessage( diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index e4184f5ce218..2614726dee51 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -23,7 +23,8 @@ import { RootListenersTracer } from "../tracers/root_listener.js"; export type RunnableFunc = ( input: RunInput, - options?: Record + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options?: any ) => RunOutput | Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any From c9d340284052e186dcb80103e4ed4ad49a7653f5 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 1 Dec 2023 10:17:51 -0800 Subject: [PATCH 20/25] cr --- examples/src/chains/retrieval_qa.ts | 2 +- examples/src/guides/expression_language/cookbook_retriever.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/src/chains/retrieval_qa.ts b/examples/src/chains/retrieval_qa.ts index 803ea724a6b2..2e8706b1f4fd 100644 --- a/examples/src/chains/retrieval_qa.ts +++ b/examples/src/chains/retrieval_qa.ts @@ -39,7 +39,7 @@ const prompt = ChatPromptTemplate.fromMessages(messages); const chain = RunnableSequence.from([ { - context: vectorStoreRetriever.pipe((docs) => formatDocumentsAsString(docs)), + context: vectorStoreRetriever.pipe(formatDocumentsAsString), question: new RunnablePassthrough(), }, prompt, diff --git a/examples/src/guides/expression_language/cookbook_retriever.ts b/examples/src/guides/expression_language/cookbook_retriever.ts index c2f33280702c..800e0d810c41 100644 --- a/examples/src/guides/expression_language/cookbook_retriever.ts +++ b/examples/src/guides/expression_language/cookbook_retriever.ts @@ -26,7 +26,7 @@ Question: {question}`); const chain = RunnableSequence.from([ { - context: retriever.pipe((docs) => formatDocumentsAsString(docs)), + context: retriever.pipe(formatDocumentsAsString), question: new RunnablePassthrough(), }, prompt, From b465628ee7f9f4dc492be16f7ad7562ea50a8ed2 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 1 Dec 2023 16:34:43 -0800 Subject: [PATCH 21/25] cr --- langchain-core/src/runnables/base.ts | 8 ++-- langchain-core/src/runnables/config.ts | 61 +++++++++++++------------ langchain-core/src/runnables/history.ts | 6 +-- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 2614726dee51..1aed404235eb 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -647,12 +647,14 @@ export class RunnableBinding< // eslint-disable-next-line @typescript-eslint/no-explicit-any options?: Record ): Promise> { - const config = mergeConfigs(this.config, options); - return mergeConfigs( + const config = mergeConfigs(this.config, options); + return mergeConfigs( config, ...(this.configFactories ? await Promise.all( - this.configFactories.map(async (f) => await f(config)) + this.configFactories.map( + async (factoryMethod) => await factoryMethod(config) + ) ) : []) ); diff --git a/langchain-core/src/runnables/config.ts b/langchain-core/src/runnables/config.ts index 6491d38ac25f..e604071a7b09 100644 --- a/langchain-core/src/runnables/config.ts +++ b/langchain-core/src/runnables/config.ts @@ -30,54 +30,59 @@ export function mergeConfigs( copy[key] = (copy[key] ?? []).concat(options[key] ?? []); } else if (key === "callbacks") { const baseCallbacks = copy.callbacks; - const theseCallbacks = options.callbacks ?? config.callbacks; + const providedCallbacks = options.callbacks ?? config.callbacks; // callbacks can be either undefined, Array or manager // so merging two callbacks values has 6 cases - if (Array.isArray(theseCallbacks)) { + if (Array.isArray(providedCallbacks)) { if (!baseCallbacks) { - copy.callbacks = theseCallbacks; + copy.callbacks = providedCallbacks; } else if (Array.isArray(baseCallbacks)) { - copy.callbacks = baseCallbacks.concat(theseCallbacks); + copy.callbacks = baseCallbacks.concat(providedCallbacks); } else { // baseCallbacks is a manager const manager = baseCallbacks.copy(); - for (const callback of theseCallbacks) { + for (const callback of providedCallbacks) { manager.addHandler(callback, true); } copy.callbacks = manager; } - } else if (theseCallbacks) { - // theseCallbacks is a manager + } else if (providedCallbacks) { + // providedCallbacks is a manager if (!baseCallbacks) { - copy.callbacks = theseCallbacks; + copy.callbacks = providedCallbacks; } else if (Array.isArray(baseCallbacks)) { - const manager = theseCallbacks.copy(); + const manager = providedCallbacks.copy(); for (const callback of baseCallbacks) { manager.addHandler(callback, true); } copy.callbacks = manager; } else { // baseCallbacks is also a manager - copy.callbacks = new CallbackManager(theseCallbacks.parentRunId, { - handlers: baseCallbacks.handlers.concat(theseCallbacks.handlers), - inheritableHandlers: baseCallbacks.inheritableHandlers.concat( - theseCallbacks.inheritableHandlers - ), - tags: Array.from( - new Set(baseCallbacks.tags.concat(theseCallbacks.tags)) - ), - inheritableTags: Array.from( - new Set( - baseCallbacks.inheritableTags.concat( - theseCallbacks.inheritableTags + copy.callbacks = new CallbackManager( + providedCallbacks.parentRunId, + { + handlers: baseCallbacks.handlers.concat( + providedCallbacks.handlers + ), + inheritableHandlers: baseCallbacks.inheritableHandlers.concat( + providedCallbacks.inheritableHandlers + ), + tags: Array.from( + new Set(baseCallbacks.tags.concat(providedCallbacks.tags)) + ), + inheritableTags: Array.from( + new Set( + baseCallbacks.inheritableTags.concat( + providedCallbacks.inheritableTags + ) ) - ) - ), - metadata: { - ...baseCallbacks.metadata, - ...theseCallbacks.metadata, - }, - }); + ), + metadata: { + ...baseCallbacks.metadata, + ...providedCallbacks.metadata, + }, + } + ); } } } else { diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index 178549ac66c5..e03a1ca37f65 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -10,10 +10,10 @@ import { Run } from "../tracers/base.js"; import { Runnable, RunnableBinding, - RunnableBindingArgs, + type RunnableBindingArgs, RunnableLambda, } from "./base.js"; -import { RunnableConfig } from "./config.js"; +import { type RunnableConfig } from "./config.js"; import { RunnablePassthrough } from "./passthrough.js"; type GetSessionHistoryCallable = ( @@ -52,7 +52,7 @@ export class RunnableWithMessageHistory< this._enterHistory(input, options ?? {}), }).withConfig({ runName: "loadHistory" }); - const messagesKey = fields.historyMessagesKey || fields.inputMessagesKey; + const messagesKey = fields.historyMessagesKey ?? fields.inputMessagesKey; if (messagesKey) { historyChain = RunnablePassthrough.assign({ [messagesKey]: historyChain, From dcbc640af32f272cb4f87bc129fef82f409d6b03 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 1 Dec 2023 17:37:24 -0800 Subject: [PATCH 22/25] cr --- langchain-core/src/runnables/base.ts | 11 +++++------ langchain-core/src/runnables/history.ts | 4 +--- langchain-core/src/runnables/tests/runnable.test.ts | 1 + 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 1aed404235eb..1c7ee6642c0f 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -726,12 +726,11 @@ export class RunnableBinding< ): Promise<(RunOutput | Error)[]> { const mergedOptions = Array.isArray(options) ? await Promise.all( - options.map( - async (individualOption) => - await this._mergeConfig({ - ...individualOption, - ...this.kwargs, - }) + options.map(async (individualOption) => + this._mergeConfig({ + ...individualOption, + ...this.kwargs, + }) ) ) : await this._mergeConfig({ ...options, ...this.kwargs }); diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index e03a1ca37f65..74140d28189a 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -13,7 +13,6 @@ import { type RunnableBindingArgs, RunnableLambda, } from "./base.js"; -import { type RunnableConfig } from "./config.js"; import { RunnablePassthrough } from "./passthrough.js"; type GetSessionHistoryCallable = ( @@ -62,8 +61,7 @@ export class RunnableWithMessageHistory< const bound = historyChain .pipe( fields.runnable.withListeners({ - onEnd: (run: Run, config: RunnableConfig = {}) => - this._exitHistory(run, config), + onEnd: (run, config) => this._exitHistory(run, config ?? {}), }) ) .withConfig({ runName: "RunnableWithMessageHistory" }); diff --git a/langchain-core/src/runnables/tests/runnable.test.ts b/langchain-core/src/runnables/tests/runnable.test.ts index fd0030e4f049..149a39112c4f 100644 --- a/langchain-core/src/runnables/tests/runnable.test.ts +++ b/langchain-core/src/runnables/tests/runnable.test.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Run } from "langsmith"; +import { jest } from "@jest/globals"; import { createChatMessageChunkEncoderStream } from "../../language_models/chat_models.js"; import { BaseMessage } from "../../messages/index.js"; import { OutputParserException } from "../../output_parsers/base.js"; From 5e113e98a0850e39ad3eaacb02bb3faa01d242a8 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 1 Dec 2023 17:54:52 -0800 Subject: [PATCH 23/25] cr --- langchain-core/src/runnables/base.ts | 6 ++++-- langchain-core/src/runnables/history.ts | 12 +++++++----- .../agents/toolkits/conversational_retrieval/tool.ts | 2 +- langchain/src/memory/vector_store.ts | 2 +- langchain/src/tools/webbrowser.ts | 2 +- langchain/src/util/document.ts | 3 +-- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 1c7ee6642c0f..208a97b3ff49 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -24,7 +24,9 @@ import { RootListenersTracer } from "../tracers/root_listener.js"; export type RunnableFunc = ( input: RunInput, // eslint-disable-next-line @typescript-eslint/no-explicit-any - options?: any + options?: Record & { + config?: RunnableConfig; + } ) => RunOutput | Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1502,7 +1504,7 @@ export class RunnableLambda extends Runnable< config?: Partial, runManager?: CallbackManagerForChainRun ) { - let output = await this.func(input, config); + let output = await this.func(input, { config }); if (output && Runnable.isRunnable(output)) { output = await output.invoke( input, diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index 74140d28189a..522e153ddd6a 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -47,8 +47,7 @@ export class RunnableWithMessageHistory< } ) { let historyChain: Runnable = new RunnableLambda({ - func: (input: any, options?: Record) => - this._enterHistory(input, options ?? {}), + func: (input, options) => this._enterHistory(input, options ?? {}), }).withConfig({ runName: "loadHistory" }); const messagesKey = fields.historyMessagesKey ?? fields.inputMessagesKey; @@ -118,9 +117,12 @@ export class RunnableWithMessageHistory< ); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - _enterHistory(input: any, config: BaseCallbackConfig): Array { - const history = config?.configurable?.messageHistory; + _enterHistory( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + input: any, + kwargs?: { config?: RunnableConfig } + ): Array { + const history = kwargs?.config?.configurable?.messageHistory; if (this.historyMessagesKey) { return history.messages; diff --git a/langchain/src/agents/toolkits/conversational_retrieval/tool.ts b/langchain/src/agents/toolkits/conversational_retrieval/tool.ts index cc62eb3b28a4..36a15e2ef9a1 100644 --- a/langchain/src/agents/toolkits/conversational_retrieval/tool.ts +++ b/langchain/src/agents/toolkits/conversational_retrieval/tool.ts @@ -19,7 +19,7 @@ export function createRetrieverTool( input, runManager?.getChild("retriever") ); - return formatDocumentsAsString(docs, "\n"); + return formatDocumentsAsString(docs); }; const schema = z.object({ input: z diff --git a/langchain/src/memory/vector_store.ts b/langchain/src/memory/vector_store.ts index 8cdbb4e412fb..a0ba15b781d3 100644 --- a/langchain/src/memory/vector_store.ts +++ b/langchain/src/memory/vector_store.ts @@ -89,7 +89,7 @@ export class VectorStoreRetrieverMemory return { [this.memoryKey]: this.returnDocs ? results - : formatDocumentsAsString(results, "\n"), + : formatDocumentsAsString(results), }; } diff --git a/langchain/src/tools/webbrowser.ts b/langchain/src/tools/webbrowser.ts index c765d4c50492..55ec8249d8a1 100644 --- a/langchain/src/tools/webbrowser.ts +++ b/langchain/src/tools/webbrowser.ts @@ -267,7 +267,7 @@ export class WebBrowser extends Tool { undefined, runManager?.getChild("vectorstore") ); - context = formatDocumentsAsString(results, "\n"); + context = formatDocumentsAsString(results); } const input = `Text:${context}\n\nI need ${ diff --git a/langchain/src/util/document.ts b/langchain/src/util/document.ts index 3867af470ef9..640e6fc9f949 100644 --- a/langchain/src/util/document.ts +++ b/langchain/src/util/document.ts @@ -9,5 +9,4 @@ import { Document } from "../document.js"; */ export const formatDocumentsAsString = ( documents: Document[], - separator = "\n\n" -): string => documents.map((doc) => doc.pageContent).join(separator); +): string => documents.map((doc) => doc.pageContent).join("\n\n"); From 52ceeea7de007b95263a049ffa00a500f71484b7 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 1 Dec 2023 17:56:18 -0800 Subject: [PATCH 24/25] cr --- langchain-core/src/runnables/history.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index 522e153ddd6a..662a1cbb4de6 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -13,6 +13,7 @@ import { type RunnableBindingArgs, RunnableLambda, } from "./base.js"; +import { RunnableConfig } from "./config.js"; import { RunnablePassthrough } from "./passthrough.js"; type GetSessionHistoryCallable = ( From f3f6c796522eb95040524360ba6f211511bb4a24 Mon Sep 17 00:00:00 2001 From: jacoblee93 Date: Fri, 1 Dec 2023 18:49:50 -0800 Subject: [PATCH 25/25] Style --- langchain-core/src/chat_history.ts | 16 ++++++---------- langchain-core/src/runnables/base.ts | 3 +-- langchain/src/util/document.ts | 5 ++--- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/langchain-core/src/chat_history.ts b/langchain-core/src/chat_history.ts index 7c17fc2f5423..979323008b3b 100644 --- a/langchain-core/src/chat_history.ts +++ b/langchain-core/src/chat_history.ts @@ -42,27 +42,23 @@ export class FakeChatMessageHistory extends BaseChatMessageHistory { super(); } - public getMessages(): Promise { - return Promise.resolve(this.messages); + async getMessages(): Promise { + return this.messages; } - public addMessage(message: BaseMessage): Promise { + async addMessage(message: BaseMessage): Promise { this.messages.push(message); - return Promise.resolve(); } - public addUserMessage(message: string): Promise { + async addUserMessage(message: string): Promise { this.messages.push(new HumanMessage(message)); - return Promise.resolve(); } - public addAIChatMessage(message: string): Promise { + async addAIChatMessage(message: string): Promise { this.messages.push(new AIMessage(message)); - return Promise.resolve(); } - public clear(): Promise { + async clear(): Promise { this.messages = []; - return Promise.resolve(); } } diff --git a/langchain-core/src/runnables/base.ts b/langchain-core/src/runnables/base.ts index 208a97b3ff49..697bc59f095a 100644 --- a/langchain-core/src/runnables/base.ts +++ b/langchain-core/src/runnables/base.ts @@ -655,7 +655,7 @@ export class RunnableBinding< ...(this.configFactories ? await Promise.all( this.configFactories.map( - async (factoryMethod) => await factoryMethod(config) + async (configFactory) => await configFactory(config) ) ) : []) @@ -798,7 +798,6 @@ export class RunnableBinding< onEnd?: (run: Run, config?: RunnableConfig) => void | Promise; onError?: (run: Run, config?: RunnableConfig) => void | Promise; }): Runnable { - // return new RunnableBinding({ bound: this.bound, kwargs: this.kwargs, diff --git a/langchain/src/util/document.ts b/langchain/src/util/document.ts index 640e6fc9f949..a1ee0afc5285 100644 --- a/langchain/src/util/document.ts +++ b/langchain/src/util/document.ts @@ -7,6 +7,5 @@ import { Document } from "../document.js"; * @param documents * @returns A string of the documents page content, separated by newlines. */ -export const formatDocumentsAsString = ( - documents: Document[], -): string => documents.map((doc) => doc.pageContent).join("\n\n"); +export const formatDocumentsAsString = (documents: Document[]): string => + documents.map((doc) => doc.pageContent).join("\n\n");