@langchain/langgraph
Version:
305 lines (304 loc) • 10.6 kB
JavaScript
const require_timeout = require("../pregel/utils/timeout.cjs");
const require_constants = require("../constants.cjs");
const require_last_value = require("../channels/last_value.cjs");
const require_utils = require("../utils.cjs");
const require_write = require("../pregel/write.cjs");
const require_read = require("../pregel/read.cjs");
require("../pregel/utils/index.cjs");
const require_call = require("../pregel/call.cjs");
const require_pregel_index = require("../pregel/index.cjs");
const require_ephemeral_value = require("../channels/ephemeral_value.cjs");
let _langchain_core_singletons = require("@langchain/core/singletons");
//#region src/func/index.ts
/**
* Define a LangGraph task using the `task` function.
*
* Tasks can only be called from within an {@link entrypoint} or from within a StateGraph.
* A task can be called like a regular function with the following differences:
*
* - When a checkpointer is enabled, the function inputs and outputs must be serializable.
* - The wrapped function can only be called from within an entrypoint or StateGraph.
* - Calling the function produces a promise. This makes it easy to parallelize tasks.
*
* @typeParam ArgsT - The type of arguments the task function accepts
* @typeParam OutputT - The type of value the task function returns
* @param optionsOrName - Either an {@link TaskOptions} object, or a string for the name of the task
* @param func - The function that executes this task
* @returns A proxy function that accepts the same arguments as the original and always returns the result as a Promise
*
* @example basic example
* ```typescript
* const addOne = task("add", async (a: number) => a + 1);
*
* const workflow = entrypoint("example", async (numbers: number[]) => {
* const promises = numbers.map(n => addOne(n));
* const results = await Promise.all(promises);
* return results;
* });
*
* // Call the entrypoint
* await workflow.invoke([1, 2, 3]); // Returns [2, 3, 4]
* ```
*
* @example using a retry policy
* ```typescript
* const addOne = task({
* name: "add",
* retry: { maxAttempts: 3 }
* },
* async (a: number) => a + 1
* );
*
* const workflow = entrypoint("example", async (numbers: number[]) => {
* const promises = numbers.map(n => addOne(n));
* const results = await Promise.all(promises);
* return results;
* });
* ```
* @category Functional API
*/
function task(optionsOrName, func) {
const options = typeof optionsOrName === "string" ? {
name: optionsOrName,
retry: void 0,
cachePolicy: void 0,
timeout: void 0
} : optionsOrName;
const { name, retry } = options;
const timeout = require_timeout.coerceTimeoutPolicy(options.timeout);
if (require_utils.isAsyncGeneratorFunction(func) || require_utils.isGeneratorFunction(func)) throw new Error("Generators are disallowed as tasks. For streaming responses, use config.write.");
const cachePolicy = options.cachePolicy ?? ("cache" in options ? options.cache : void 0);
let cache;
if (typeof cachePolicy === "boolean") cache = cachePolicy ? {} : void 0;
else cache = cachePolicy;
return (...args) => {
return require_call.call({
func,
name,
retry,
cache,
timeout
}, ...args);
};
}
/**
* Define a LangGraph workflow using the `entrypoint` function.
*
* ### Function signature
*
* The wrapped function must accept at most **two parameters**. The first parameter
* is the input to the function. The second (optional) parameter is a
* {@link LangGraphRunnableConfig} object. If you wish to pass multiple parameters to
* the function, you can pass them as an object.
*
* ### Helper functions
*
* #### Streaming
* To write data to the "custom" stream, use the {@link getWriter} function, or the
* {@link LangGraphRunnableConfig.writer} property.
*
* #### State management
* The {@link getPreviousState} function can be used to access the previous state
* that was returned from the last invocation of the entrypoint on the same thread id.
*
* If you wish to save state other than the return value, you can use the
* {@link entrypoint.final} function.
*
* @typeParam InputT - The type of input the entrypoint accepts
* @typeParam OutputT - The type of output the entrypoint produces
* @param optionsOrName - Either an {@link EntrypointOptions} object, or a string for the name of the entrypoint
* @param func - The function that executes this entrypoint
* @returns A {@link Pregel} instance that can be run to execute the workflow
*
* @example Using entrypoint and tasks
* ```typescript
* import { task, entrypoint } from "@langchain/langgraph";
* import { MemorySaver } from "@langchain/langgraph-checkpoint";
* import { interrupt, Command } from "@langchain/langgraph";
*
* const composeEssay = task("compose", async (topic: string) => {
* await new Promise(r => setTimeout(r, 1000)); // Simulate slow operation
* return `An essay about ${topic}`;
* });
*
* const reviewWorkflow = entrypoint({
* name: "review",
* checkpointer: new MemorySaver()
* }, async (topic: string) => {
* const essay = await composeEssay(topic);
* const humanReview = await interrupt({
* question: "Please provide a review",
* essay
* });
* return {
* essay,
* review: humanReview
* };
* });
*
* // Example configuration for the workflow
* const config = {
* configurable: {
* thread_id: "some_thread"
* }
* };
*
* // Topic for the essay
* const topic = "cats";
*
* // Stream the workflow to generate the essay and await human review
* for await (const result of reviewWorkflow.stream(topic, config)) {
* console.log(result);
* }
*
* // Example human review provided after the interrupt
* const humanReview = "This essay is great.";
*
* // Resume the workflow with the provided human review
* for await (const result of reviewWorkflow.stream(new Command({ resume: humanReview }), config)) {
* console.log(result);
* }
* ```
*
* @example Accessing the previous return value
* ```typescript
* import { entrypoint, getPreviousState } from "@langchain/langgraph";
* import { MemorySaver } from "@langchain/langgraph-checkpoint";
*
* const accumulator = entrypoint({
* name: "accumulator",
* checkpointer: new MemorySaver()
* }, async (input: string) => {
* const previous = getPreviousState<number>();
* return previous !== undefined ? `${previous } ${input}` : input;
* });
*
* const config = {
* configurable: {
* thread_id: "some_thread"
* }
* };
* await accumulator.invoke("hello", config); // returns "hello"
* await accumulator.invoke("world", config); // returns "hello world"
* ```
*
* @example Using entrypoint.final to save a value
* ```typescript
* import { entrypoint, getPreviousState } from "@langchain/langgraph";
* import { MemorySaver } from "@langchain/langgraph-checkpoint";
*
* const myWorkflow = entrypoint({
* name: "accumulator",
* checkpointer: new MemorySaver()
* }, async (num: number) => {
* const previous = getPreviousState<number>();
*
* // This will return the previous value to the caller, saving
* // 2 * num to the checkpoint, which will be used in the next invocation
* // for the `previous` parameter.
* return entrypoint.final({
* value: previous ?? 0,
* save: 2 * num
* });
* });
*
* const config = {
* configurable: {
* thread_id: "some_thread"
* }
* };
*
* await myWorkflow.invoke(3, config); // 0 (previous was undefined)
* await myWorkflow.invoke(1, config); // 6 (previous was 3 * 2 from the previous invocation)
* ```
* @category Functional API
*/
const entrypoint = function entrypoint(optionsOrName, func) {
const { name, checkpointer, store, cache } = typeof optionsOrName === "string" ? {
name: optionsOrName,
checkpointer: void 0,
store: void 0
} : optionsOrName;
const timeout = require_timeout.coerceTimeoutPolicy(typeof optionsOrName === "string" ? void 0 : optionsOrName.timeout);
if (require_utils.isAsyncGeneratorFunction(func) || require_utils.isGeneratorFunction(func)) throw new Error("Generators are disallowed as entrypoints. For streaming responses, use config.write.");
const streamMode = "updates";
const bound = require_call.getRunnableForEntrypoint(name, func);
function isEntrypointFinal(value) {
return typeof value === "object" && value !== null && "__lg_type" in value && value.__lg_type === "__pregel_final";
}
const pluckReturnValue = new require_utils.RunnableCallable({
name: "pluckReturnValue",
func: (value) => {
return isEntrypointFinal(value) ? value.value : value;
}
});
const pluckSaveValue = new require_utils.RunnableCallable({
name: "pluckSaveValue",
func: (value) => {
return isEntrypointFinal(value) ? value.save : value;
}
});
const entrypointNode = new require_read.PregelNode({
bound,
triggers: [require_constants.START],
channels: [require_constants.START],
timeout,
writers: [new require_write.ChannelWrite([{
channel: require_constants.END,
value: require_write.PASSTHROUGH,
mapper: pluckReturnValue
}, {
channel: require_constants.PREVIOUS,
value: require_write.PASSTHROUGH,
mapper: pluckSaveValue
}], [require_constants.TAG_HIDDEN])]
});
return new require_pregel_index.Pregel({
name,
checkpointer,
nodes: { [name]: entrypointNode },
channels: {
[require_constants.START]: new require_ephemeral_value.EphemeralValue(),
[require_constants.END]: new require_last_value.LastValue(),
[require_constants.PREVIOUS]: new require_last_value.LastValue()
},
inputChannels: require_constants.START,
outputChannels: require_constants.END,
streamChannels: require_constants.END,
streamMode,
store,
cache
});
};
entrypoint.final = function final({ value, save }) {
return {
value,
save,
__lg_type: "__pregel_final"
};
};
/**
* A helper utility function for use with the functional API that returns the previous
* state from the checkpoint from the last invocation of the current thread.
*
* This function allows workflows to access state that was saved in previous runs
* using {@link entrypoint.final}.
*
* @typeParam StateT - The type of the state that was previously saved
* @returns The previous saved state from the last invocation of the current thread
*
* @example
* ```typescript
* const previousState = getPreviousState<{ counter: number }>();
* const newCount = (previousState?.counter ?? 0) + 1;
* ```
* @category Functional API
*/
function getPreviousState() {
return _langchain_core_singletons.AsyncLocalStorageProviderSingleton.getRunnableConfig().configurable?.[require_constants.CONFIG_KEY_PREVIOUS_STATE];
}
//#endregion
exports.entrypoint = entrypoint;
exports.getPreviousState = getPreviousState;
exports.task = task;
//# sourceMappingURL=index.cjs.map