@langchain/core
Version:
Core LangChain.js abstractions and schemas
153 lines (151 loc) • 5.37 kB
JavaScript
import { getCallbackManagerForConfig, patchConfig } from "./config.js";
import { concat } from "../utils/stream.js";
import { Runnable, _coerceToDict, _coerceToRunnable } from "./base.js";
//#region src/runnables/branch.ts
/**
* Class that represents a runnable branch. The RunnableBranch is
* initialized with an array of branches and a default branch. When invoked,
* it evaluates the condition of each branch in order and executes the
* corresponding branch if the condition is true. If none of the conditions
* are true, it executes the default branch.
* @example
* ```typescript
* const branch = RunnableBranch.from([
* [
* (x: { topic: string; question: string }) =>
* x.topic.toLowerCase().includes("anthropic"),
* anthropicChain,
* ],
* [
* (x: { topic: string; question: string }) =>
* x.topic.toLowerCase().includes("langchain"),
* langChainChain,
* ],
* generalChain,
* ]);
*
* const fullChain = RunnableSequence.from([
* {
* topic: classificationChain,
* question: (input: { question: string }) => input.question,
* },
* branch,
* ]);
*
* const result = await fullChain.invoke({
* question: "how do I use LangChain?",
* });
* ```
*/
var RunnableBranch = class extends Runnable {
static lc_name() {
return "RunnableBranch";
}
lc_namespace = ["langchain_core", "runnables"];
lc_serializable = true;
default;
branches;
constructor(fields) {
super(fields);
this.branches = fields.branches;
this.default = fields.default;
}
/**
* Convenience method for instantiating a RunnableBranch from
* RunnableLikes (objects, functions, or Runnables).
*
* Each item in the input except for the last one should be a
* tuple with two items. The first is a "condition" RunnableLike that
* returns "true" if the second RunnableLike in the tuple should run.
*
* The final item in the input should be a RunnableLike that acts as a
* default branch if no other branches match.
*
* @example
* ```ts
* import { RunnableBranch } from "@langchain/core/runnables";
*
* const branch = RunnableBranch.from([
* [(x: number) => x > 0, (x: number) => x + 1],
* [(x: number) => x < 0, (x: number) => x - 1],
* (x: number) => x
* ]);
* ```
* @param branches An array where the every item except the last is a tuple of [condition, runnable]
* pairs. The last item is a default runnable which is invoked if no other condition matches.
* @returns A new RunnableBranch.
*/
static from(branches) {
if (branches.length < 1) throw new Error("RunnableBranch requires at least one branch");
const branchLikes = branches.slice(0, -1);
const coercedBranches = branchLikes.map(([condition, runnable]) => [_coerceToRunnable(condition), _coerceToRunnable(runnable)]);
const defaultBranch = _coerceToRunnable(branches[branches.length - 1]);
return new this({
branches: coercedBranches,
default: defaultBranch
});
}
async _invoke(input, config, runManager) {
let result;
for (let i = 0; i < this.branches.length; i += 1) {
const [condition, branchRunnable] = this.branches[i];
const conditionValue = await condition.invoke(input, patchConfig(config, { callbacks: runManager?.getChild(`condition:${i + 1}`) }));
if (conditionValue) {
result = await branchRunnable.invoke(input, patchConfig(config, { callbacks: runManager?.getChild(`branch:${i + 1}`) }));
break;
}
}
if (!result) result = await this.default.invoke(input, patchConfig(config, { callbacks: runManager?.getChild("branch:default") }));
return result;
}
async invoke(input, config = {}) {
return this._callWithConfig(this._invoke, input, config);
}
async *_streamIterator(input, config) {
const callbackManager_ = await getCallbackManagerForConfig(config);
const runManager = await callbackManager_?.handleChainStart(this.toJSON(), _coerceToDict(input, "input"), config?.runId, void 0, void 0, void 0, config?.runName);
let finalOutput;
let finalOutputSupported = true;
let stream;
try {
for (let i = 0; i < this.branches.length; i += 1) {
const [condition, branchRunnable] = this.branches[i];
const conditionValue = await condition.invoke(input, patchConfig(config, { callbacks: runManager?.getChild(`condition:${i + 1}`) }));
if (conditionValue) {
stream = await branchRunnable.stream(input, patchConfig(config, { callbacks: runManager?.getChild(`branch:${i + 1}`) }));
for await (const chunk of stream) {
yield chunk;
if (finalOutputSupported) if (finalOutput === void 0) finalOutput = chunk;
else try {
finalOutput = concat(finalOutput, chunk);
} catch {
finalOutput = void 0;
finalOutputSupported = false;
}
}
break;
}
}
if (stream === void 0) {
stream = await this.default.stream(input, patchConfig(config, { callbacks: runManager?.getChild("branch:default") }));
for await (const chunk of stream) {
yield chunk;
if (finalOutputSupported) if (finalOutput === void 0) finalOutput = chunk;
else try {
finalOutput = concat(finalOutput, chunk);
} catch {
finalOutput = void 0;
finalOutputSupported = false;
}
}
}
} catch (e) {
await runManager?.handleChainError(e);
throw e;
}
await runManager?.handleChainEnd(finalOutput ?? {});
}
};
//#endregion
export { RunnableBranch };
//# sourceMappingURL=branch.js.map