@langchain/langgraph
Version:
195 lines (194 loc) • 7.53 kB
JavaScript
const require_constants = require("../constants.cjs");
const require_config = require("./utils/config.cjs");
const require_io = require("./io.cjs");
const require_subgraph = require("./utils/subgraph.cjs");
let _langchain_langgraph_checkpoint = require("@langchain/langgraph-checkpoint");
//#region src/pregel/debug.ts
const COLORS_MAP = {
blue: {
start: "\x1B[34m",
end: "\x1B[0m"
},
green: {
start: "\x1B[32m",
end: "\x1B[0m"
},
yellow: {
start: "\x1B[33;1m",
end: "\x1B[0m"
}
};
/**
* Wrap some text in a color for printing to the console.
*/
const wrap = (color, text) => `${color.start}${text}${color.end}`;
/**
* Build the user-meaningful metadata to forward on a task's stream payload.
*
* Drops langgraph's internal framework keys ({@link EXCLUDED_METADATA_KEYS}) —
* which are redundant with the task's own fields and namespace — while keeping
* keys like `lc_agent_name`, `ls_integration`, and any user-supplied metadata.
* Filtered config tags are folded in under `tags`, mirroring the messages
* stream handler. Returns `undefined` when there is nothing to forward.
*/
function buildTaskMetadata(config) {
if (config == null) return void 0;
const metadata = {};
if (config.metadata != null) {
for (const [key, value] of Object.entries(config.metadata)) if (!_langchain_langgraph_checkpoint.EXCLUDED_METADATA_KEYS.has(key)) metadata[key] = value;
}
const filteredTags = require_config.filterToUserTags(config.tags);
if (filteredTags != null) metadata.tags = filteredTags;
return Object.keys(metadata).length > 0 ? metadata : void 0;
}
function* mapDebugTasks(tasks) {
for (const { id, name, input, config, triggers, writes } of tasks) {
if (config?.tags?.includes("langsmith:hidden")) continue;
const payload = {
id,
name,
input,
triggers,
interrupts: writes.filter(([writeId, n]) => {
return writeId === id && n === "__interrupt__";
}).map(([, v]) => {
return v;
})
};
const metadata = buildTaskMetadata(config);
if (metadata != null) payload.metadata = metadata;
yield payload;
}
}
function isMultipleChannelWrite(value) {
if (typeof value !== "object" || value === null) return false;
return "$writes" in value && Array.isArray(value.$writes);
}
function mapTaskResultWrites(writes) {
const result = {};
for (const [channel, value] of writes) {
const strChannel = String(channel);
if (strChannel in result) {
const channelWrites = isMultipleChannelWrite(result[strChannel]) ? result[strChannel].$writes : [result[strChannel]];
channelWrites.push(value);
result[strChannel] = { $writes: channelWrites };
} else result[strChannel] = value;
}
return result;
}
function* mapDebugTaskResults(tasks, streamChannels) {
for (const [{ id, name, config }, writes] of tasks) {
if (config?.tags?.includes("langsmith:hidden")) continue;
yield {
id,
name,
result: mapTaskResultWrites(writes.filter(([channel]) => {
return Array.isArray(streamChannels) ? streamChannels.includes(channel) : channel === streamChannels;
})),
interrupts: writes.filter((w) => w[0] === require_constants.INTERRUPT).map((w) => w[1])
};
}
}
function* mapDebugCheckpoint(config, channels, streamChannels, metadata, tasks, pendingWrites, parentConfig, outputKeys) {
function formatConfig(config) {
const pyConfig = {};
if (config.callbacks != null) pyConfig.callbacks = config.callbacks;
if (config.configurable != null) pyConfig.configurable = config.configurable;
if (config.maxConcurrency != null) pyConfig.max_concurrency = config.maxConcurrency;
if (config.metadata != null) pyConfig.metadata = config.metadata;
if (config.recursionLimit != null) pyConfig.recursion_limit = config.recursionLimit;
if (config.runId != null) pyConfig.run_id = config.runId;
if (config.runName != null) pyConfig.run_name = config.runName;
if (config.tags != null) pyConfig.tags = config.tags;
return pyConfig;
}
const parentNs = config.configurable?.checkpoint_ns;
const taskStates = {};
for (const task of tasks) {
if (!(task.subgraphs?.length ? task.subgraphs : [task.proc]).find(require_subgraph.findSubgraphPregel)) continue;
let taskNs = `${task.name}:${task.id}`;
if (parentNs) taskNs = `${parentNs}|${taskNs}`;
taskStates[task.id] = { configurable: {
thread_id: config.configurable?.thread_id,
checkpoint_ns: taskNs
} };
}
yield {
config: formatConfig(config),
values: require_io.readChannels(channels, streamChannels),
metadata,
next: tasks.map((task) => task.name),
tasks: tasksWithWrites(tasks, pendingWrites, taskStates, outputKeys),
parentConfig: parentConfig ? formatConfig(parentConfig) : void 0
};
}
function tasksWithWrites(tasks, pendingWrites, states, outputKeys) {
return tasks.map((task) => {
const error = pendingWrites.find(([id, n]) => id === task.id && n === "__error__")?.[2];
const interrupts = pendingWrites.filter(([id, n]) => id === task.id && n === "__interrupt__").map(([, , v]) => v);
const result = (() => {
if (error || interrupts.length || !pendingWrites.length) return void 0;
const idx = pendingWrites.findIndex(([tid, n]) => tid === task.id && n === "__return__");
if (idx >= 0) return pendingWrites[idx][2];
if (typeof outputKeys === "string") return pendingWrites.find(([tid, n]) => tid === task.id && n === outputKeys)?.[2];
if (Array.isArray(outputKeys)) {
const results = pendingWrites.filter(([tid, n]) => tid === task.id && outputKeys.includes(n)).map(([, n, v]) => [n, v]);
if (!results.length) return void 0;
return mapTaskResultWrites(results);
}
})();
if (error) return {
id: task.id,
name: task.name,
path: task.path,
error,
interrupts,
result
};
const taskState = states?.[task.id];
return {
id: task.id,
name: task.name,
path: task.path,
interrupts,
...taskState !== void 0 ? { state: taskState } : {},
result
};
});
}
function printStepCheckpoint(step, channels, whitelist) {
console.log([
`${wrap(COLORS_MAP.blue, `[${step}:checkpoint]`)}`,
`\x1b[1m State at the end of step ${step}:\x1b[0m\n`,
JSON.stringify(require_io.readChannels(channels, whitelist), null, 2)
].join(""));
}
function printStepTasks(step, nextTasks) {
const nTasks = nextTasks.length;
console.log([
`${wrap(COLORS_MAP.blue, `[${step}:tasks]`)}`,
`\x1b[1m Starting step ${step} with ${nTasks} task${nTasks === 1 ? "" : "s"}:\x1b[0m\n`,
nextTasks.map((task) => `- ${wrap(COLORS_MAP.green, String(task.name))} -> ${JSON.stringify(task.input, null, 2)}`).join("\n")
].join(""));
}
function printStepWrites(step, writes, whitelist) {
const byChannel = {};
for (const [channel, value] of writes) if (whitelist.includes(channel)) {
if (!byChannel[channel]) byChannel[channel] = [];
byChannel[channel].push(value);
}
console.log([
`${wrap(COLORS_MAP.blue, `[${step}:writes]`)}`,
`\x1b[1m Finished step ${step} with writes to ${Object.keys(byChannel).length} channel${Object.keys(byChannel).length !== 1 ? "s" : ""}:\x1b[0m\n`,
Object.entries(byChannel).map(([name, vals]) => `- ${wrap(COLORS_MAP.yellow, name)} -> ${vals.map((v) => JSON.stringify(v)).join(", ")}`).join("\n")
].join(""));
}
//#endregion
exports.mapDebugCheckpoint = mapDebugCheckpoint;
exports.mapDebugTaskResults = mapDebugTaskResults;
exports.mapDebugTasks = mapDebugTasks;
exports.printStepCheckpoint = printStepCheckpoint;
exports.printStepTasks = printStepTasks;
exports.printStepWrites = printStepWrites;
exports.tasksWithWrites = tasksWithWrites;
//# sourceMappingURL=debug.cjs.map