UNPKG

@langchain/core

Version:
1,327 lines (1,326 loc) 67.1 kB
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs'); const require_load_serializable = require('../load/serializable.cjs'); const require_utils = require('../tools/utils.cjs'); const require_index = require('../singletons/async_local_storage/index.cjs'); require('../singletons/index.cjs'); const require_config = require('./config.cjs'); const require_signal = require('../utils/signal.cjs'); const require_utils_stream = require('../utils/stream.cjs'); const require_tracers_log_stream = require('../tracers/log_stream.cjs'); const require_event_stream = require('../tracers/event_stream.cjs'); const require_utils_async_caller = require('../utils/async_caller.cjs'); const require_root_listener = require('../tracers/root_listener.cjs'); const require_utils$1 = require('./utils.cjs'); const require_zod = require('../utils/types/zod.cjs'); const require_runnables_graph = require('./graph.cjs'); const require_wrappers = require('./wrappers.cjs'); const require_iter = require('./iter.cjs'); const zod_v3 = require_rolldown_runtime.__toESM(require("zod/v3")); const p_retry = require_rolldown_runtime.__toESM(require("p-retry")); const uuid = require_rolldown_runtime.__toESM(require("uuid")); const langsmith_singletons_traceable = require_rolldown_runtime.__toESM(require("langsmith/singletons/traceable")); //#region src/runnables/base.ts function _coerceToDict(value, defaultKey) { return value && !Array.isArray(value) && !(value instanceof Date) && typeof value === "object" ? value : { [defaultKey]: value }; } /** * A Runnable is a generic unit of work that can be invoked, batched, streamed, and/or * transformed. */ var Runnable = class extends require_load_serializable.Serializable { lc_runnable = true; name; getName(suffix) { const name = this.name ?? this.constructor.lc_name() ?? this.constructor.name; return suffix ? `${name}${suffix}` : name; } /** * Add retry logic to an existing runnable. * @param fields.stopAfterAttempt The number of attempts to retry. * @param fields.onFailedAttempt A function that is called when a retry fails. * @returns A new RunnableRetry that, when invoked, will retry according to the parameters. */ withRetry(fields) { return new RunnableRetry({ bound: this, kwargs: {}, config: {}, maxAttemptNumber: fields?.stopAfterAttempt, ...fields }); } /** * Bind config to a Runnable, returning a new Runnable. * @param config New configuration parameters to attach to the new runnable. * @returns A new RunnableBinding with a config matching what's passed. */ withConfig(config) { return new RunnableBinding({ bound: this, config, kwargs: {} }); } /** * Create a new runnable from the current one that will try invoking * other passed fallback runnables if the initial invocation fails. * @param fields.fallbacks Other runnables to call if the runnable errors. * @returns A new RunnableWithFallbacks. */ withFallbacks(fields) { const fallbacks = Array.isArray(fields) ? fields : fields.fallbacks; return new RunnableWithFallbacks({ runnable: this, fallbacks }); } _getOptionsList(options, length = 0) { if (Array.isArray(options) && options.length !== length) throw new Error(`Passed "options" must be an array with the same length as the inputs, but got ${options.length} options for ${length} inputs`); if (Array.isArray(options)) return options.map(require_config.ensureConfig); if (length > 1 && !Array.isArray(options) && options.runId) { console.warn("Provided runId will be used only for the first element of the batch."); const subsequent = Object.fromEntries(Object.entries(options).filter(([key]) => key !== "runId")); return Array.from({ length }, (_, i) => require_config.ensureConfig(i === 0 ? options : subsequent)); } return Array.from({ length }, () => require_config.ensureConfig(options)); } async batch(inputs, options, batchOptions) { const configList = this._getOptionsList(options ?? {}, inputs.length); const maxConcurrency = configList[0]?.maxConcurrency ?? batchOptions?.maxConcurrency; const caller = new require_utils_async_caller.AsyncCaller({ maxConcurrency, onFailedAttempt: (e) => { throw e; } }); const batchCalls = inputs.map((input, i) => caller.call(async () => { try { const result = await this.invoke(input, configList[i]); return result; } catch (e) { if (batchOptions?.returnExceptions) return e; throw e; } })); return Promise.all(batchCalls); } /** * Default streaming implementation. * Subclasses should override this method if they support streaming output. * @param input * @param options */ async *_streamIterator(input, options) { yield this.invoke(input, options); } /** * Stream output in chunks. * @param input * @param options * @returns A readable stream that is also an iterable. */ async stream(input, options) { const config = require_config.ensureConfig(options); const wrappedGenerator = new require_utils_stream.AsyncGeneratorWithSetup({ generator: this._streamIterator(input, config), config }); await wrappedGenerator.setup; return require_utils_stream.IterableReadableStream.fromAsyncGenerator(wrappedGenerator); } _separateRunnableConfigFromCallOptions(options) { let runnableConfig; if (options === void 0) runnableConfig = require_config.ensureConfig(options); else runnableConfig = require_config.ensureConfig({ callbacks: options.callbacks, tags: options.tags, metadata: options.metadata, runName: options.runName, configurable: options.configurable, recursionLimit: options.recursionLimit, maxConcurrency: options.maxConcurrency, runId: options.runId, timeout: options.timeout, signal: options.signal }); const callOptions = { ...options }; delete callOptions.callbacks; delete callOptions.tags; delete callOptions.metadata; delete callOptions.runName; delete callOptions.configurable; delete callOptions.recursionLimit; delete callOptions.maxConcurrency; delete callOptions.runId; delete callOptions.timeout; delete callOptions.signal; return [runnableConfig, callOptions]; } async _callWithConfig(func, input, options) { const config = require_config.ensureConfig(options); const callbackManager_ = await require_config.getCallbackManagerForConfig(config); const runManager = await callbackManager_?.handleChainStart(this.toJSON(), _coerceToDict(input, "input"), config.runId, config?.runType, void 0, void 0, config?.runName ?? this.getName()); delete config.runId; let output; try { const promise = func.call(this, input, config, runManager); output = await require_signal.raceWithSignal(promise, options?.signal); } catch (e) { await runManager?.handleChainError(e); throw e; } await runManager?.handleChainEnd(_coerceToDict(output, "output")); return output; } /** * Internal method that handles batching and configuration for a runnable * It takes a function, input values, and optional configuration, and * returns a promise that resolves to the output values. * @param func The function to be executed for each input value. * @param input The input values to be processed. * @param config Optional configuration for the function execution. * @returns A promise that resolves to the output values. */ async _batchWithConfig(func, inputs, options, batchOptions) { const optionsList = this._getOptionsList(options ?? {}, inputs.length); const callbackManagers = await Promise.all(optionsList.map(require_config.getCallbackManagerForConfig)); const runManagers = await Promise.all(callbackManagers.map(async (callbackManager, i) => { const handleStartRes = await callbackManager?.handleChainStart(this.toJSON(), _coerceToDict(inputs[i], "input"), optionsList[i].runId, optionsList[i].runType, void 0, void 0, optionsList[i].runName ?? this.getName()); delete optionsList[i].runId; return handleStartRes; })); let outputs; try { const promise = func.call(this, inputs, optionsList, runManagers, batchOptions); outputs = await require_signal.raceWithSignal(promise, optionsList?.[0]?.signal); } catch (e) { await Promise.all(runManagers.map((runManager) => runManager?.handleChainError(e))); throw e; } await Promise.all(runManagers.map((runManager) => runManager?.handleChainEnd(_coerceToDict(outputs, "output")))); return outputs; } /** @internal */ _concatOutputChunks(first, second) { return require_utils_stream.concat(first, second); } /** * Helper method to transform an Iterator of Input values into an Iterator of * Output values, with callbacks. * Use this to implement `stream()` or `transform()` in Runnable subclasses. */ async *_transformStreamWithConfig(inputGenerator, transformer, options) { let finalInput; let finalInputSupported = true; let finalOutput; let finalOutputSupported = true; const config = require_config.ensureConfig(options); const callbackManager_ = await require_config.getCallbackManagerForConfig(config); const outerThis = this; async function* wrapInputForTracing() { for await (const chunk of inputGenerator) { if (finalInputSupported) if (finalInput === void 0) finalInput = chunk; else try { finalInput = outerThis._concatOutputChunks(finalInput, chunk); } catch { finalInput = void 0; finalInputSupported = false; } yield chunk; } } let runManager; try { const pipe = await require_utils_stream.pipeGeneratorWithSetup(transformer.bind(this), wrapInputForTracing(), async () => callbackManager_?.handleChainStart(this.toJSON(), { input: "" }, config.runId, config.runType, void 0, void 0, config.runName ?? this.getName()), options?.signal, config); delete config.runId; runManager = pipe.setup; const streamEventsHandler = runManager?.handlers.find(require_event_stream.isStreamEventsHandler); let iterator = pipe.output; if (streamEventsHandler !== void 0 && runManager !== void 0) iterator = streamEventsHandler.tapOutputIterable(runManager.runId, iterator); const streamLogHandler = runManager?.handlers.find(require_tracers_log_stream.isLogStreamHandler); if (streamLogHandler !== void 0 && runManager !== void 0) iterator = streamLogHandler.tapOutputIterable(runManager.runId, iterator); for await (const chunk of iterator) { yield chunk; if (finalOutputSupported) if (finalOutput === void 0) finalOutput = chunk; else try { finalOutput = this._concatOutputChunks(finalOutput, chunk); } catch { finalOutput = void 0; finalOutputSupported = false; } } } catch (e) { await runManager?.handleChainError(e, void 0, void 0, void 0, { inputs: _coerceToDict(finalInput, "input") }); throw e; } await runManager?.handleChainEnd(finalOutput ?? {}, void 0, void 0, void 0, { inputs: _coerceToDict(finalInput, "input") }); } getGraph(_) { const graph = new require_runnables_graph.Graph(); const inputNode = graph.addNode({ name: `${this.getName()}Input`, schema: zod_v3.z.any() }); const runnableNode = graph.addNode(this); const outputNode = graph.addNode({ name: `${this.getName()}Output`, schema: zod_v3.z.any() }); graph.addEdge(inputNode, runnableNode); graph.addEdge(runnableNode, outputNode); return graph; } /** * Create a new runnable sequence that runs each individual runnable in series, * piping the output of one runnable into another runnable or runnable-like. * @param coerceable A runnable, function, or object whose values are functions or runnables. * @returns A new runnable sequence. */ pipe(coerceable) { return new RunnableSequence({ first: this, last: _coerceToRunnable(coerceable) }); } /** * Pick keys from the dict output of this runnable. Returns a new runnable. */ pick(keys) { return this.pipe(new RunnablePick(keys)); } /** * Assigns new fields to the dict output of this runnable. Returns a new runnable. */ assign(mapping) { return this.pipe(new RunnableAssign(new RunnableMap({ steps: mapping }))); } /** * Default implementation of transform, which buffers input and then calls stream. * Subclasses should override this method if they can start producing output while * input is still being generated. * @param generator * @param options */ async *transform(generator, options) { let finalChunk; for await (const chunk of generator) if (finalChunk === void 0) finalChunk = chunk; else finalChunk = this._concatOutputChunks(finalChunk, chunk); yield* this._streamIterator(finalChunk, require_config.ensureConfig(options)); } /** * Stream all output from a runnable, as reported to the callback system. * This includes all inner runs of LLMs, Retrievers, Tools, etc. * Output is streamed as Log objects, which include a list of * jsonpatch ops that describe how the state of the run has changed in each * step, and the final state of the run. * The jsonpatch ops can be applied in order to construct state. * @param input * @param options * @param streamOptions */ async *streamLog(input, options, streamOptions) { const logStreamCallbackHandler = new require_tracers_log_stream.LogStreamCallbackHandler({ ...streamOptions, autoClose: false, _schemaFormat: "original" }); const config = require_config.ensureConfig(options); yield* this._streamLog(input, logStreamCallbackHandler, config); } async *_streamLog(input, logStreamCallbackHandler, config) { const { callbacks } = config; if (callbacks === void 0) config.callbacks = [logStreamCallbackHandler]; else if (Array.isArray(callbacks)) config.callbacks = callbacks.concat([logStreamCallbackHandler]); else { const copiedCallbacks = callbacks.copy(); copiedCallbacks.addHandler(logStreamCallbackHandler, true); config.callbacks = copiedCallbacks; } const runnableStreamPromise = this.stream(input, config); async function consumeRunnableStream() { try { const runnableStream = await runnableStreamPromise; for await (const chunk of runnableStream) { const patch = new require_tracers_log_stream.RunLogPatch({ ops: [{ op: "add", path: "/streamed_output/-", value: chunk }] }); await logStreamCallbackHandler.writer.write(patch); } } finally { await logStreamCallbackHandler.writer.close(); } } const runnableStreamConsumePromise = consumeRunnableStream(); try { for await (const log of logStreamCallbackHandler) yield log; } finally { await runnableStreamConsumePromise; } } streamEvents(input, options, streamOptions) { let stream; if (options.version === "v1") stream = this._streamEventsV1(input, options, streamOptions); else if (options.version === "v2") stream = this._streamEventsV2(input, options, streamOptions); else throw new Error(`Only versions "v1" and "v2" of the schema are currently supported.`); if (options.encoding === "text/event-stream") return require_wrappers.convertToHttpEventStream(stream); else return require_utils_stream.IterableReadableStream.fromAsyncGenerator(stream); } async *_streamEventsV2(input, options, streamOptions) { const eventStreamer = new require_event_stream.EventStreamCallbackHandler({ ...streamOptions, autoClose: false }); const config = require_config.ensureConfig(options); const runId = config.runId ?? (0, uuid.v4)(); config.runId = runId; const callbacks = config.callbacks; if (callbacks === void 0) config.callbacks = [eventStreamer]; else if (Array.isArray(callbacks)) config.callbacks = callbacks.concat(eventStreamer); else { const copiedCallbacks = callbacks.copy(); copiedCallbacks.addHandler(eventStreamer, true); config.callbacks = copiedCallbacks; } const abortController = new AbortController(); const outerThis = this; async function consumeRunnableStream() { let signal; let listener = null; try { if (options?.signal) if ("any" in AbortSignal) signal = AbortSignal.any([abortController.signal, options.signal]); else { signal = options.signal; listener = () => { abortController.abort(); }; options.signal.addEventListener("abort", listener, { once: true }); } else signal = abortController.signal; const runnableStream = await outerThis.stream(input, { ...config, signal }); const tappedStream = eventStreamer.tapOutputIterable(runId, runnableStream); for await (const _ of tappedStream) if (abortController.signal.aborted) break; } finally { await eventStreamer.finish(); if (signal && listener) signal.removeEventListener("abort", listener); } } const runnableStreamConsumePromise = consumeRunnableStream(); let firstEventSent = false; let firstEventRunId; try { for await (const event of eventStreamer) { if (!firstEventSent) { event.data.input = input; firstEventSent = true; firstEventRunId = event.run_id; yield event; continue; } if (event.run_id === firstEventRunId && event.event.endsWith("_end")) { if (event.data?.input) delete event.data.input; } yield event; } } finally { abortController.abort(); await runnableStreamConsumePromise; } } async *_streamEventsV1(input, options, streamOptions) { let runLog; let hasEncounteredStartEvent = false; const config = require_config.ensureConfig(options); const rootTags = config.tags ?? []; const rootMetadata = config.metadata ?? {}; const rootName = config.runName ?? this.getName(); const logStreamCallbackHandler = new require_tracers_log_stream.LogStreamCallbackHandler({ ...streamOptions, autoClose: false, _schemaFormat: "streaming_events" }); const rootEventFilter = new require_utils$1._RootEventFilter({ ...streamOptions }); const logStream = this._streamLog(input, logStreamCallbackHandler, config); for await (const log of logStream) { if (!runLog) runLog = require_tracers_log_stream.RunLog.fromRunLogPatch(log); else runLog = runLog.concat(log); if (runLog.state === void 0) throw new Error(`Internal error: "streamEvents" state is missing. Please open a bug report.`); if (!hasEncounteredStartEvent) { hasEncounteredStartEvent = true; const state$2 = { ...runLog.state }; const event = { run_id: state$2.id, event: `on_${state$2.type}_start`, name: rootName, tags: rootTags, metadata: rootMetadata, data: { input } }; if (rootEventFilter.includeEvent(event, state$2.type)) yield event; } const paths = log.ops.filter((op) => op.path.startsWith("/logs/")).map((op) => op.path.split("/")[2]); const dedupedPaths = [...new Set(paths)]; for (const path of dedupedPaths) { let eventType; let data = {}; const logEntry = runLog.state.logs[path]; if (logEntry.end_time === void 0) if (logEntry.streamed_output.length > 0) eventType = "stream"; else eventType = "start"; else eventType = "end"; if (eventType === "start") { if (logEntry.inputs !== void 0) data.input = logEntry.inputs; } else if (eventType === "end") { if (logEntry.inputs !== void 0) data.input = logEntry.inputs; data.output = logEntry.final_output; } else if (eventType === "stream") { const chunkCount = logEntry.streamed_output.length; if (chunkCount !== 1) throw new Error(`Expected exactly one chunk of streamed output, got ${chunkCount} instead. Encountered in: "${logEntry.name}"`); data = { chunk: logEntry.streamed_output[0] }; logEntry.streamed_output = []; } yield { event: `on_${logEntry.type}_${eventType}`, name: logEntry.name, run_id: logEntry.id, tags: logEntry.tags, metadata: logEntry.metadata, data }; } const { state: state$1 } = runLog; if (state$1.streamed_output.length > 0) { const chunkCount = state$1.streamed_output.length; if (chunkCount !== 1) throw new Error(`Expected exactly one chunk of streamed output, got ${chunkCount} instead. Encountered in: "${state$1.name}"`); const data = { chunk: state$1.streamed_output[0] }; state$1.streamed_output = []; const event = { event: `on_${state$1.type}_stream`, run_id: state$1.id, tags: rootTags, metadata: rootMetadata, name: rootName, data }; if (rootEventFilter.includeEvent(event, state$1.type)) yield event; } } const state = runLog?.state; if (state !== void 0) { const event = { event: `on_${state.type}_end`, name: rootName, run_id: state.id, tags: rootTags, metadata: rootMetadata, data: { output: state.final_output } }; if (rootEventFilter.includeEvent(event, state.type)) yield event; } } static isRunnable(thing) { return require_utils$1.isRunnableInterface(thing); } /** * 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 }) { return new RunnableBinding({ bound: this, config: {}, configFactories: [(config) => ({ callbacks: [new require_root_listener.RootListenersTracer({ config, onStart, onEnd, onError })] })] }); } /** * Convert a runnable to a tool. Return a new instance of `RunnableToolLike` * which contains the runnable, name, description and schema. * * @template {T extends RunInput = RunInput} RunInput - The input type of the runnable. Should be the same as the `RunInput` type of the runnable. * * @param fields * @param {string | undefined} [fields.name] The name of the tool. If not provided, it will default to the name of the runnable. * @param {string | undefined} [fields.description] The description of the tool. Falls back to the description on the Zod schema if not provided, or undefined if neither are provided. * @param {z.ZodType<T>} [fields.schema] The Zod schema for the input of the tool. Infers the Zod type from the input type of the runnable. * @returns {RunnableToolLike<z.ZodType<T>, RunOutput>} An instance of `RunnableToolLike` which is a runnable that can be used as a tool. */ asTool(fields) { return convertRunnableToTool(this, fields); } }; /** * Wraps a runnable and applies partial config upon invocation. * * @example * ```typescript * import { * type RunnableConfig, * RunnableLambda, * } from "@langchain/core/runnables"; * * const enhanceProfile = ( * profile: Record<string, any>, * config?: RunnableConfig * ) => { * if (config?.configurable?.role) { * return { ...profile, role: config.configurable.role }; * } * return profile; * }; * * const runnable = RunnableLambda.from(enhanceProfile); * * // Bind configuration to the runnable to set the user's role dynamically * const adminRunnable = runnable.withConfig({ configurable: { role: "Admin" } }); * const userRunnable = runnable.withConfig({ configurable: { role: "User" } }); * * const result1 = await adminRunnable.invoke({ * name: "Alice", * email: "alice@example.com" * }); * * // { name: "Alice", email: "alice@example.com", role: "Admin" } * * const result2 = await userRunnable.invoke({ * name: "Bob", * email: "bob@example.com" * }); * * // { name: "Bob", email: "bob@example.com", role: "User" } * ``` */ var RunnableBinding = class RunnableBinding extends Runnable { static lc_name() { return "RunnableBinding"; } lc_namespace = ["langchain_core", "runnables"]; lc_serializable = true; bound; config; kwargs; configFactories; constructor(fields) { super(fields); this.bound = fields.bound; this.kwargs = fields.kwargs; this.config = fields.config; this.configFactories = fields.configFactories; } getName(suffix) { return this.bound.getName(suffix); } async _mergeConfig(...options) { const config = require_config.mergeConfigs(this.config, ...options); return require_config.mergeConfigs(config, ...this.configFactories ? await Promise.all(this.configFactories.map(async (configFactory) => await configFactory(config))) : []); } withConfig(config) { return new this.constructor({ bound: this.bound, kwargs: this.kwargs, config: { ...this.config, ...config } }); } withRetry(fields) { return new RunnableRetry({ bound: this.bound, kwargs: this.kwargs, config: this.config, maxAttemptNumber: fields?.stopAfterAttempt, ...fields }); } async invoke(input, options) { return this.bound.invoke(input, await this._mergeConfig(options, this.kwargs)); } async batch(inputs, options, batchOptions) { const mergedOptions = Array.isArray(options) ? await Promise.all(options.map(async (individualOption) => this._mergeConfig(require_config.ensureConfig(individualOption), this.kwargs))) : await this._mergeConfig(require_config.ensureConfig(options), this.kwargs); return this.bound.batch(inputs, mergedOptions, batchOptions); } /** @internal */ _concatOutputChunks(first, second) { return this.bound._concatOutputChunks(first, second); } async *_streamIterator(input, options) { yield* this.bound._streamIterator(input, await this._mergeConfig(require_config.ensureConfig(options), this.kwargs)); } async stream(input, options) { return this.bound.stream(input, await this._mergeConfig(require_config.ensureConfig(options), this.kwargs)); } async *transform(generator, options) { yield* this.bound.transform(generator, await this._mergeConfig(require_config.ensureConfig(options), this.kwargs)); } streamEvents(input, options, streamOptions) { const outerThis = this; const generator = async function* () { yield* outerThis.bound.streamEvents(input, { ...await outerThis._mergeConfig(require_config.ensureConfig(options), outerThis.kwargs), version: options.version }, streamOptions); }; return require_utils_stream.IterableReadableStream.fromAsyncGenerator(generator()); } static isRunnableBinding(thing) { 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 }) { return new RunnableBinding({ bound: this.bound, kwargs: this.kwargs, config: this.config, configFactories: [(config) => ({ callbacks: [new require_root_listener.RootListenersTracer({ config, onStart, onEnd, onError })] })] }); } }; /** * A runnable that delegates calls to another runnable * with each element of the input sequence. * @example * ```typescript * import { RunnableEach, RunnableLambda } from "@langchain/core/runnables"; * * const toUpperCase = (input: string): string => input.toUpperCase(); * const addGreeting = (input: string): string => `Hello, ${input}!`; * * const upperCaseLambda = RunnableLambda.from(toUpperCase); * const greetingLambda = RunnableLambda.from(addGreeting); * * const chain = new RunnableEach({ * bound: upperCaseLambda.pipe(greetingLambda), * }); * * const result = await chain.invoke(["alice", "bob", "carol"]) * * // ["Hello, ALICE!", "Hello, BOB!", "Hello, CAROL!"] * ``` */ var RunnableEach = class RunnableEach extends Runnable { static lc_name() { return "RunnableEach"; } lc_serializable = true; lc_namespace = ["langchain_core", "runnables"]; bound; constructor(fields) { super(fields); this.bound = fields.bound; } /** * Invokes the runnable with the specified input and configuration. * @param input The input to invoke the runnable with. * @param config The configuration to invoke the runnable with. * @returns A promise that resolves to the output of the runnable. */ async invoke(inputs, config) { return this._callWithConfig(this._invoke.bind(this), inputs, config); } /** * A helper method that is used to invoke the runnable with the specified input and configuration. * @param input The input to invoke the runnable with. * @param config The configuration to invoke the runnable with. * @returns A promise that resolves to the output of the runnable. */ async _invoke(inputs, config, runManager) { return this.bound.batch(inputs, require_config.patchConfig(config, { callbacks: 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 }) { return new RunnableEach({ bound: this.bound.withListeners({ onStart, onEnd, onError }) }); } }; /** * Base class for runnables that can be retried a * specified number of times. * @example * ```typescript * import { * RunnableLambda, * RunnableRetry, * } from "@langchain/core/runnables"; * * // Simulate an API call that fails * const simulateApiCall = (input: string): string => { * console.log(`Attempting API call with input: ${input}`); * throw new Error("API call failed due to network issue"); * }; * * const apiCallLambda = RunnableLambda.from(simulateApiCall); * * // Apply retry logic using the .withRetry() method * const apiCallWithRetry = apiCallLambda.withRetry({ stopAfterAttempt: 3 }); * * // Alternatively, create a RunnableRetry instance manually * const manualRetry = new RunnableRetry({ * bound: apiCallLambda, * maxAttemptNumber: 3, * config: {}, * }); * * // Example invocation using the .withRetry() method * const res = await apiCallWithRetry * .invoke("Request 1") * .catch((error) => { * console.error("Failed after multiple retries:", error.message); * }); * * // Example invocation using the manual retry instance * const res2 = await manualRetry * .invoke("Request 2") * .catch((error) => { * console.error("Failed after multiple retries:", error.message); * }); * ``` */ var RunnableRetry = class extends RunnableBinding { static lc_name() { return "RunnableRetry"; } lc_namespace = ["langchain_core", "runnables"]; maxAttemptNumber = 3; onFailedAttempt = () => {}; constructor(fields) { super(fields); this.maxAttemptNumber = fields.maxAttemptNumber ?? this.maxAttemptNumber; this.onFailedAttempt = fields.onFailedAttempt ?? this.onFailedAttempt; } _patchConfigForRetry(attempt, config, runManager) { const tag = attempt > 1 ? `retry:attempt:${attempt}` : void 0; return require_config.patchConfig(config, { callbacks: runManager?.getChild(tag) }); } async _invoke(input, config, runManager) { return (0, p_retry.default)((attemptNumber) => super.invoke(input, this._patchConfigForRetry(attemptNumber, config, runManager)), { onFailedAttempt: (error) => this.onFailedAttempt(error, input), retries: Math.max(this.maxAttemptNumber - 1, 0), randomize: true }); } /** * Method that invokes the runnable with the specified input, run manager, * and config. It handles the retry logic by catching any errors and * recursively invoking itself with the updated config for the next retry * attempt. * @param input The input for the runnable. * @param runManager The run manager for the runnable. * @param config The config for the runnable. * @returns A promise that resolves to the output of the runnable. */ async invoke(input, config) { return this._callWithConfig(this._invoke.bind(this), input, config); } async _batch(inputs, configs, runManagers, batchOptions) { const resultsMap = {}; try { await (0, p_retry.default)(async (attemptNumber) => { const remainingIndexes = inputs.map((_, i) => i).filter((i) => resultsMap[i.toString()] === void 0 || resultsMap[i.toString()] instanceof Error); const remainingInputs = remainingIndexes.map((i) => inputs[i]); const patchedConfigs = remainingIndexes.map((i) => this._patchConfigForRetry(attemptNumber, configs?.[i], runManagers?.[i])); const results = await super.batch(remainingInputs, patchedConfigs, { ...batchOptions, returnExceptions: true }); let firstException; for (let i = 0; i < results.length; i += 1) { const result = results[i]; const resultMapIndex = remainingIndexes[i]; if (result instanceof Error) { if (firstException === void 0) { firstException = result; firstException.input = remainingInputs[i]; } } resultsMap[resultMapIndex.toString()] = result; } if (firstException) throw firstException; return results; }, { onFailedAttempt: (error) => this.onFailedAttempt(error, error.input), retries: Math.max(this.maxAttemptNumber - 1, 0), randomize: true }); } catch (e) { if (batchOptions?.returnExceptions !== true) throw e; } return Object.keys(resultsMap).sort((a, b) => parseInt(a, 10) - parseInt(b, 10)).map((key) => resultsMap[parseInt(key, 10)]); } async batch(inputs, options, batchOptions) { return this._batchWithConfig(this._batch.bind(this), inputs, options, batchOptions); } }; /** * A sequence of runnables, where the output of each is the input of the next. * @example * ```typescript * const promptTemplate = PromptTemplate.fromTemplate( * "Tell me a joke about {topic}", * ); * const chain = RunnableSequence.from([promptTemplate, new ChatOpenAI({ model: "gpt-4o-mini" })]); * const result = await chain.invoke({ topic: "bears" }); * ``` */ var RunnableSequence = class RunnableSequence extends Runnable { static lc_name() { return "RunnableSequence"; } first; middle = []; last; omitSequenceTags = false; lc_serializable = true; lc_namespace = ["langchain_core", "runnables"]; constructor(fields) { super(fields); this.first = fields.first; this.middle = fields.middle ?? this.middle; this.last = fields.last; this.name = fields.name; this.omitSequenceTags = fields.omitSequenceTags ?? this.omitSequenceTags; } get steps() { return [ this.first, ...this.middle, this.last ]; } async invoke(input, options) { const config = require_config.ensureConfig(options); const callbackManager_ = await require_config.getCallbackManagerForConfig(config); const runManager = await callbackManager_?.handleChainStart(this.toJSON(), _coerceToDict(input, "input"), config.runId, void 0, void 0, void 0, config?.runName); delete config.runId; let nextStepInput = input; let finalOutput; try { const initialSteps = [this.first, ...this.middle]; for (let i = 0; i < initialSteps.length; i += 1) { const step = initialSteps[i]; const promise = step.invoke(nextStepInput, require_config.patchConfig(config, { callbacks: runManager?.getChild(this.omitSequenceTags ? void 0 : `seq:step:${i + 1}`) })); nextStepInput = await require_signal.raceWithSignal(promise, options?.signal); } if (options?.signal?.aborted) throw require_signal.getAbortSignalError(options.signal); finalOutput = await this.last.invoke(nextStepInput, require_config.patchConfig(config, { callbacks: runManager?.getChild(this.omitSequenceTags ? void 0 : `seq:step:${this.steps.length}`) })); } catch (e) { await runManager?.handleChainError(e); throw e; } await runManager?.handleChainEnd(_coerceToDict(finalOutput, "output")); return finalOutput; } async batch(inputs, options, batchOptions) { const configList = this._getOptionsList(options ?? {}, inputs.length); const callbackManagers = await Promise.all(configList.map(require_config.getCallbackManagerForConfig)); const runManagers = await Promise.all(callbackManagers.map(async (callbackManager, i) => { const handleStartRes = await callbackManager?.handleChainStart(this.toJSON(), _coerceToDict(inputs[i], "input"), configList[i].runId, void 0, void 0, void 0, configList[i].runName); delete configList[i].runId; return handleStartRes; })); let nextStepInputs = inputs; try { for (let i = 0; i < this.steps.length; i += 1) { const step = this.steps[i]; const promise = step.batch(nextStepInputs, runManagers.map((runManager, j) => { const childRunManager = runManager?.getChild(this.omitSequenceTags ? void 0 : `seq:step:${i + 1}`); return require_config.patchConfig(configList[j], { callbacks: childRunManager }); }), batchOptions); nextStepInputs = await require_signal.raceWithSignal(promise, configList[0]?.signal); } } catch (e) { await Promise.all(runManagers.map((runManager) => runManager?.handleChainError(e))); throw e; } await Promise.all(runManagers.map((runManager) => runManager?.handleChainEnd(_coerceToDict(nextStepInputs, "output")))); return nextStepInputs; } /** @internal */ _concatOutputChunks(first, second) { return this.last._concatOutputChunks(first, second); } async *_streamIterator(input, options) { const callbackManager_ = await require_config.getCallbackManagerForConfig(options); const { runId,...otherOptions } = options ?? {}; const runManager = await callbackManager_?.handleChainStart(this.toJSON(), _coerceToDict(input, "input"), runId, void 0, void 0, void 0, otherOptions?.runName); const steps = [ this.first, ...this.middle, this.last ]; let concatSupported = true; let finalOutput; async function* inputGenerator() { yield input; } try { let finalGenerator = steps[0].transform(inputGenerator(), require_config.patchConfig(otherOptions, { callbacks: runManager?.getChild(this.omitSequenceTags ? void 0 : `seq:step:1`) })); for (let i = 1; i < steps.length; i += 1) { const step = steps[i]; finalGenerator = await step.transform(finalGenerator, require_config.patchConfig(otherOptions, { callbacks: runManager?.getChild(this.omitSequenceTags ? void 0 : `seq:step:${i + 1}`) })); } for await (const chunk of finalGenerator) { options?.signal?.throwIfAborted(); yield chunk; if (concatSupported) if (finalOutput === void 0) finalOutput = chunk; else try { finalOutput = this._concatOutputChunks(finalOutput, chunk); } catch { finalOutput = void 0; concatSupported = false; } } } catch (e) { await runManager?.handleChainError(e); throw e; } await runManager?.handleChainEnd(_coerceToDict(finalOutput, "output")); } getGraph(config) { const graph = new require_runnables_graph.Graph(); let currentLastNode = null; this.steps.forEach((step, index) => { const stepGraph = step.getGraph(config); if (index !== 0) stepGraph.trimFirstNode(); if (index !== this.steps.length - 1) stepGraph.trimLastNode(); graph.extend(stepGraph); const stepFirstNode = stepGraph.firstNode(); if (!stepFirstNode) throw new Error(`Runnable ${step} has no first node`); if (currentLastNode) graph.addEdge(currentLastNode, stepFirstNode); currentLastNode = stepGraph.lastNode(); }); return graph; } pipe(coerceable) { if (RunnableSequence.isRunnableSequence(coerceable)) return new RunnableSequence({ first: this.first, middle: this.middle.concat([ this.last, coerceable.first, ...coerceable.middle ]), last: coerceable.last, name: this.name ?? coerceable.name }); else return new RunnableSequence({ first: this.first, middle: [...this.middle, this.last], last: _coerceToRunnable(coerceable), name: this.name }); } static isRunnableSequence(thing) { return Array.isArray(thing.middle) && Runnable.isRunnable(thing); } static from([first, ...runnables], nameOrFields) { let extra = {}; if (typeof nameOrFields === "string") extra.name = nameOrFields; else if (nameOrFields !== void 0) extra = nameOrFields; return new RunnableSequence({ ...extra, first: _coerceToRunnable(first), middle: runnables.slice(0, -1).map(_coerceToRunnable), last: _coerceToRunnable(runnables[runnables.length - 1]) }); } }; /** * A runnable that runs a mapping of runnables in parallel, * and returns a mapping of their outputs. * @example * ```typescript * const mapChain = RunnableMap.from({ * joke: PromptTemplate.fromTemplate("Tell me a joke about {topic}").pipe( * new ChatAnthropic({}), * ), * poem: PromptTemplate.fromTemplate("write a 2-line poem about {topic}").pipe( * new ChatAnthropic({}), * ), * }); * const result = await mapChain.invoke({ topic: "bear" }); * ``` */ var RunnableMap = class RunnableMap extends Runnable { static lc_name() { return "RunnableMap"; } lc_namespace = ["langchain_core", "runnables"]; lc_serializable = true; steps; getStepsKeys() { return Object.keys(this.steps); } constructor(fields) { super(fields); this.steps = {}; for (const [key, value] of Object.entries(fields.steps)) this.steps[key] = _coerceToRunnable(value); } static from(steps) { return new RunnableMap({ steps }); } async invoke(input, options) { const config = require_config.ensureConfig(options); const callbackManager_ = await require_config.getCallbackManagerForConfig(config); const runManager = await callbackManager_?.handleChainStart(this.toJSON(), { input }, config.runId, void 0, void 0, void 0, config?.runName); delete config.runId; const output = {}; try { const promises = Object.entries(this.steps).map(async ([key, runnable]) => { output[key] = await runnable.invoke(input, require_config.patchConfig(config, { callbacks: runManager?.getChild(`map:key:${key}`) })); }); await require_signal.raceWithSignal(Promise.all(promises), options?.signal); } catch (e) { await runManager?.handleChainError(e); throw e; } await runManager?.handleChainEnd(output); return output; } async *_transform(generator, runManager, options) { const steps = { ...this.steps }; const inputCopies = require_utils_stream.atee(generator, Object.keys(steps).length); const tasks = new Map(Object.entries(steps).map(([key, runnable], i) => { const gen = runnable.transform(inputCopies[i], require_config.patchConfig(options, { callbacks: runManager?.getChild(`map:key:${key}`) })); return [key, gen.next().then((result) => ({ key, gen, result }))]; })); while (tasks.size) { const promise = Promise.race(tasks.values()); const { key, result, gen } = await require_signal.raceWithSignal(promise, options?.signal); tasks.delete(key); if (!result.done) { yield { [key]: result.value }; tasks.set(key, gen.next().then((result$1) => ({ key, gen, result: result$1 }))); } } } transform(generator, options) { return this._transformStreamWithConfig(generator, this._transform.bind(this), options); } async stream(input, options) { async function* generator() { yield input; } const config = require_config.ensureConfig(options); const wrappedGenerator = new require_utils_stream.AsyncGeneratorWithSetup({ generator: this.transform(generator(), config), config }); await wrappedGenerator.setup; return require_utils_stream.IterableReadableStream.fromAsyncGenerator(wrappedGenerator); } }; /** * A runnable that wraps a traced LangSmith function. */ var RunnableTraceable = class RunnableTraceable extends Runnable { lc_serializable = false; lc_namespace = ["langchain_core", "runnables"]; func; constructor(fields) { super(fields); if (!(0, langsmith_singletons_traceable.isTraceableFunction)(fields.func)) throw new Error("RunnableTraceable requires a function that is wrapped in traceable higher-order function"); this.func = fields.func; } async invoke(input, options) { const [config] = this._getOptionsList(options ?? {}, 1); const callbacks = await require_config.getCallbackManagerForConfig(config); const promise = this.func(require_config.patchConfig(config, { callbacks }), input); return require_signal.raceWithSignal(promise, config?.signal); } async *_streamIterator(input, options) { const [config] = this._getOptionsList(options ?? {}, 1); const result = await this.invoke(input, options); if (require_iter.isAsyncIterable(result)) { for await (const item of result) { config?.signal?.throwIfAborted(); yield item; } return; } if (require_iter.isIterator(result)) { while (true) { config?.signal?.throwIfAborted(); const state = result.next(); if (state.done) break; yield state.value; } return; } yield result; } static from(func) { return new RunnableTraceable({ func }); } }; function assertNonTraceableFunction(func) { if ((0, langsmith_singletons_traceable.isTraceableFunction)(func)) throw new Error("RunnableLambda requires a function that is not wrapped in traceable higher-order function. This shouldn't happen."); } /** * A runnable that wraps an arbitrary function that takes a single argument. * @example * ```typescript * import { RunnableLambda } from "@langchain/core/runnables"; * * const add = (input: { x: number; y: number }) => input.x + input.y; * * const multiply = (input: { value: number; multiplier: number }) => * input.value * input.multiplier; * * // Create runnables for the functions * const addLambda = RunnableLambda.from(add); * const multiplyLambda = RunnableLambda.from(multiply); * * // Chain the lambdas for a mathematical operation * const chainedLambda = addLambda.pipe((result) => * multiplyLambda.invoke({ value: result, multiplier: 2 }) * ); * * // Example invocation of the chainedLambda * const result = await chainedLambda.invoke({ x: 2, y: 3 }); * * // Will log "10" (since (2 + 3) * 2 = 10) * ``` */ var RunnableLambda = class RunnableLambda extends Runnable { static lc_name() { return "RunnableLambda"; } lc_namespace = ["langchain_core", "runnables"]; func; constructor(fields) { if ((0, langsmith_singletons_traceable.isTraceableFunction)(fields.func)) return RunnableTraceable.from(fields.func); super(fields); assertNonTraceableFunction(fields.func); this.func = fields.func; } static from(func) { return new RunnableLambda({ func }); } async _invoke(input, config, runManager) { return new Promise((resolve, reject) => { const childConfig = require_config.patchConfig(config, { callbacks: runManager?.getChild(), recursionLimit: (config?.recursionLimit ?? require_config.DEFAULT_RECURSION_LIMIT) - 1 }); require_index.AsyncLocalStorageProviderSingleton.runWithConfig(require_config.pickRunnableConfigKeys(childConfig), async () => { try { let output = await this.func(input, { ...childConfig }); if (output && Runnable.isRunnable(output)) { if (config?.recursionLimit === 0) throw new Error("Recursion limit reached."); output = await output.invoke(input, { ...childConfig, recursionLimit: (childConfig.recursionLimit ?? require_config.DEFAULT_RECURSION_LIMIT) - 1 }); } else if (require_iter.isAsyncIterable(output)) { let finalOutput; for await (const chunk of require_iter.consumeAsyncIterableInContext(childConfig, output)) { config?.signal?.throwIfAborted(); if (finalOutput === void 0) finalOutput = chunk; else try { finalOutput = this._concatOutputChunks(finalOutput, chunk); } catch { finalOutput = chunk; } } output = finalOutput; } else if (require_iter.isIterableIterator(output)) { let finalOutput; for (const chunk of require_iter.consumeIteratorInContext(childConfig, output)) { config?.signal?.throwIfAborted(); if (finalOutput === void 0) finalOutput = chunk; else try { finalOutput = this._concatOutputChunks(finalOutput, chunk); } catch { finalOutput = chunk; } } output = finalOutput; } resolve(output); } catch (e) { reject(e); } }); }); } async invoke(input, options) { return this._callWithConfig(this._invoke.bind(this), input, options); } async *_transform(generator, runManager, config) { let finalChunk; for await (const chunk of generator) if (finalChunk === void 0) finalChunk = chunk; else try { finalChunk = this._concatOutputChunks(finalChunk, chunk); } catch { finalChunk = chunk; } const childConfig = require_config.patchConfig(config, { callbacks: runManager?.getChild(), recursionLimit: (config?.recursionLimit ?? require_config.DEFAULT_RECURSION_LIMIT) - 1 }); const output = await new Promise((resolve, reject) => { require_index.AsyncLocalStorageProviderSingleton.runWithConfig(require_config.pickRunnableConfigKeys(childConfig), async () => { try { const res = await this.func(finalChunk, { ...childConfig, config: childConfig }); resolve(res); } catch (e) { reject(e); } }); }); if (output && Runnable.isRunnable(output)) { if (config?.recursionLimit === 0) throw new Error("Recursion limit reached."); const stream = await output.stream(finalChun