UNPKG

@langchain/langgraph

Version:
252 lines (251 loc) 11 kB
const require_constants = require("../../constants.cjs"); let _langchain_core_singletons = require("@langchain/core/singletons"); let _langchain_core_callbacks_manager = require("@langchain/core/callbacks/manager"); //#region src/pregel/utils/config.ts const COPIABLE_KEYS = [ "tags", "metadata", "callbacks", "configurable" ]; const CONFIG_KEYS = [ "tags", "metadata", "callbacks", "runName", "maxConcurrency", "recursionLimit", "configurable", "runId", "outputKeys", "streamMode", "store", "writer", "interrupt", "context", "interruptBefore", "interruptAfter", "checkpointDuring", "durability", "signal", "heartbeat", "executionInfo", "serverInfo", "control" ]; const DEFAULT_RECURSION_LIMIT = 25; const PROPAGATE_TO_METADATA = new Set([ "thread_id", "checkpoint_id", "checkpoint_ns", "task_id", "run_id", "assistant_id", "graph_id" ]); function propagateConfigurableToMetadata(configurable, metadata) { if (!configurable) return metadata; const result = metadata ?? {}; for (const key of PROPAGATE_TO_METADATA) { if (key in result) continue; const value = configurable[key]; if (value !== void 0) result[key] = value; } return result; } /** * Drop langgraph's internal `seq:step*` bookkeeping tags. * * `seq:step:N` tags are added internally to mark sequence steps; everything * else (user-supplied tags and any other framework tags) is kept. Returns the * surviving tags, or `undefined` if none remain. Shared by the stream handlers * (e.g. {@link mapDebugTasks}) so the same tag set is surfaced consistently. */ function filterToUserTags(tags) { if (tags == null || tags.length === 0) return void 0; const filtered = tags.filter((tag) => !tag.startsWith("seq:step")); return filtered.length > 0 ? filtered : void 0; } /** * Merge two `callbacks` values across configs. * * A `callbacks` value may be `undefined`, an array of handlers, or a * {@link CallbackManager}, so merging two of them has six cases. This * mirrors the callbacks branch of langchain-core's `mergeConfigs` and * langgraph's `_merge_callbacks`, so a handler bound via * `.withConfig({ callbacks: [...] })` is preserved when a later config * (e.g. `streamEvents` injecting its own internal handler) is merged on * top instead of overwriting it. */ function mergeCallbacks(base, provided) { if (provided === void 0) return base; if (base === void 0) return Array.isArray(provided) ? [...provided] : provided.copy(); if (Array.isArray(provided)) { if (Array.isArray(base)) return base.concat(provided); const manager = base.copy(); for (const callback of provided) manager.addHandler((0, _langchain_core_callbacks_manager.ensureHandler)(callback), true); return manager; } if (Array.isArray(base)) { const manager = provided.copy(); for (const callback of base) manager.addHandler((0, _langchain_core_callbacks_manager.ensureHandler)(callback), true); return manager; } return new _langchain_core_callbacks_manager.CallbackManager(provided._parentRunId, { handlers: base.handlers.concat(provided.handlers), inheritableHandlers: base.inheritableHandlers.concat(provided.inheritableHandlers), tags: Array.from(new Set(base.tags.concat(provided.tags))), inheritableTags: Array.from(new Set(base.inheritableTags.concat(provided.inheritableTags))), metadata: { ...base.metadata, ...provided.metadata }, inheritableMetadata: { ...base.inheritableMetadata, ...provided.inheritableMetadata } }); } /** * True when the caller is starting a fresh top-level run (invoke-time * `thread_id`, no active nesting keys). In that case the ambient `configurable` * from `AsyncLocalStorage` cannot be trusted per-key — it may belong to another * concurrent invocation on a shared singleton agent (scratchpad/task-input as * well as arbitrary user keys like `tenant_id`/`user_id`). The whole ambient * `configurable` is therefore ignored; any value the caller actually wants for * this run arrives through the explicit (bound + invoke-time) configs instead. * * Only the last caller-supplied config is treated as invoke-time options. * Earlier entries are graph-bound defaults from `.withConfig()` / compile and * must not count — a child graph bound with `thread_id` and invoked from a * parent task without a fresh config still needs ambient nesting keys from ALS. */ function isRootLevelExplicitInvoke(configs) { let invokeConfig; for (let i = configs.length - 1; i >= 0; i -= 1) if (configs[i] !== void 0) { invokeConfig = configs[i]; break; } const hasInvokeTimeThreadId = invokeConfig?.configurable?.thread_id !== void 0; const hasExplicitNesting = configs.some((c) => c?.configurable?.[require_constants.CONFIG_KEY_READ] !== void 0); const hasAmbientNesting = (_langchain_core_singletons.AsyncLocalStorageProviderSingleton.getRunnableConfig()?.configurable)?.[require_constants.CONFIG_KEY_READ] !== void 0; return hasInvokeTimeThreadId && !hasExplicitNesting && !hasAmbientNesting; } function ensureLangGraphConfig(...configs) { const empty = { tags: [], metadata: {}, callbacks: void 0, recursionLimit: DEFAULT_RECURSION_LIMIT, configurable: {} }; const skipImplicitConfigurable = isRootLevelExplicitInvoke(configs); const implicitConfig = _langchain_core_singletons.AsyncLocalStorageProviderSingleton.getRunnableConfig(); if (implicitConfig !== void 0) { for (const [k, v] of Object.entries(implicitConfig)) if (v !== void 0) { if (k === "configurable" && skipImplicitConfigurable) continue; if (COPIABLE_KEYS.includes(k)) { let copiedValue; if (Array.isArray(v)) copiedValue = [...v]; else if (typeof v === "object") if (k === "callbacks" && "copy" in v && typeof v.copy === "function") copiedValue = v.copy(); else copiedValue = { ...v }; else copiedValue = v; empty[k] = copiedValue; } else empty[k] = v; } } for (const config of configs) { if (config === void 0) continue; for (const [k, v] of Object.entries(config)) { if (v === void 0 || !CONFIG_KEYS.includes(k)) continue; if (k === "configurable") empty.configurable = { ...empty.configurable, ...v }; else if (k === "metadata") empty.metadata = { ...empty.metadata, ...v }; else if (k === "tags") empty.tags = [...empty.tags ?? [], ...v]; else if (k === "callbacks") empty.callbacks = mergeCallbacks(empty.callbacks, v); else empty[k] = v; } } empty.metadata = propagateConfigurableToMetadata(empty.configurable, empty.metadata) ?? {}; return empty; } /** * A helper utility function that returns the {@link BaseStore} that was set when the graph was initialized * * @returns a reference to the {@link BaseStore} that was set when the graph was initialized */ function getStore(config) { const runConfig = config ?? _langchain_core_singletons.AsyncLocalStorageProviderSingleton.getRunnableConfig(); if (runConfig === void 0) throw new Error(["Config not retrievable. This is likely because you are running in an environment without support for AsyncLocalStorage.", "If you're running `getStore` in such environment, pass the `config` from the node function directly."].join("\n")); return runConfig?.store; } /** * A helper utility function that returns the {@link LangGraphRunnableConfig#writer} if "custom" stream mode is enabled, otherwise undefined. * * @returns a reference to the {@link LangGraphRunnableConfig#writer} if "custom" stream mode is enabled, otherwise undefined */ function getWriter(config) { const runConfig = config ?? _langchain_core_singletons.AsyncLocalStorageProviderSingleton.getRunnableConfig(); if (runConfig === void 0) throw new Error(["Config not retrievable. This is likely because you are running in an environment without support for AsyncLocalStorage.", "If you're running `getWriter` in such environment, pass the `config` from the node function directly."].join("\n")); return runConfig?.writer || runConfig?.configurable?.writer; } /** * A helper utility function that returns the {@link LangGraphRunnableConfig} that was set when the graph was initialized. * * Note: This only works when running in an environment that supports node:async_hooks and AsyncLocalStorage. If you're running this in a * web environment, access the LangGraphRunnableConfig from the node function directly. * * @returns the {@link LangGraphRunnableConfig} that was set when the graph was initialized */ function getConfig() { return _langchain_core_singletons.AsyncLocalStorageProviderSingleton.getRunnableConfig(); } /** * A helper utility function that returns the input for the currently executing * task. * * Note: When called without arguments, this relies on `node:async_hooks` / * `AsyncLocalStorage`, which is available in many JavaScript environments * (Node.js, Deno, Cloudflare Workers) but not in web browsers. In environments * without `AsyncLocalStorage` support, pass the `config` that your node/tool * function receives directly, e.g. `getCurrentTaskInput(config)`. * * Tip: Inside a tool run by a `ToolNode`, prefer reading graph state from * `runtime.state` on the second tool argument (typed as `ToolRuntime` from * `@langchain/core/tools`). It works in every runtime, including web browsers. * * @param config - Optional {@link LangGraphRunnableConfig} to read the task * input from. Provide this when running in an environment without * `AsyncLocalStorage` support (e.g. web browsers). * @returns the input for the currently executing task */ function getCurrentTaskInput(config) { const runConfig = config ?? _langchain_core_singletons.AsyncLocalStorageProviderSingleton.getRunnableConfig(); if (runConfig === void 0) throw new Error(["Config not retrievable. This is likely because you are running in an environment without support for AsyncLocalStorage.", "If you're running `getCurrentTaskInput` in such environment, pass the `config` from the node function directly."].join("\n")); if (runConfig.configurable?.["__pregel_scratchpad"]?.currentTaskInput === void 0) throw new Error("BUG: internal scratchpad not initialized."); return runConfig.configurable[require_constants.CONFIG_KEY_SCRATCHPAD].currentTaskInput; } function recastCheckpointNamespace(namespace) { return namespace.split("|").filter((part) => !part.match(/^\d+$/)).map((part) => part.split(":")[0]).join("|"); } function getParentCheckpointNamespace(namespace) { const parts = namespace.split("|"); while (parts.length > 1 && parts[parts.length - 1].match(/^\d+$/)) parts.pop(); return parts.slice(0, -1).join("|"); } //#endregion exports.ensureLangGraphConfig = ensureLangGraphConfig; exports.filterToUserTags = filterToUserTags; exports.getConfig = getConfig; exports.getCurrentTaskInput = getCurrentTaskInput; exports.getParentCheckpointNamespace = getParentCheckpointNamespace; exports.getStore = getStore; exports.getWriter = getWriter; exports.propagateConfigurableToMetadata = propagateConfigurableToMetadata; exports.recastCheckpointNamespace = recastCheckpointNamespace; //# sourceMappingURL=config.cjs.map