UNPKG

@langchain/langgraph

Version:
1 lines 16.8 kB
{"version":3,"file":"debug.cjs","names":["EXCLUDED_METADATA_KEYS","filterToUserTags","INTERRUPT","findSubgraphPregel","readChannels"],"sources":["../../src/pregel/debug.ts"],"sourcesContent":["import { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n CheckpointMetadata,\n CheckpointPendingWrite,\n EXCLUDED_METADATA_KEYS,\n PendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\nimport { BaseChannel } from \"../channels/base.js\";\nimport {\n ERROR,\n Interrupt,\n INTERRUPT,\n RETURN,\n TAG_HIDDEN,\n} from \"../constants.js\";\nimport { EmptyChannelError } from \"../errors.js\";\nimport {\n PregelExecutableTask,\n PregelTaskDescription,\n StateSnapshot,\n} from \"./types.js\";\nimport { readChannels } from \"./io.js\";\nimport { findSubgraphPregel } from \"./utils/subgraph.js\";\nimport { filterToUserTags } from \"./utils/config.js\";\n\ntype ConsoleColors = {\n start: string;\n end: string;\n};\n\ntype ConsoleColorMap = {\n [key: string]: ConsoleColors;\n};\n\nconst COLORS_MAP: ConsoleColorMap = {\n blue: {\n start: \"\\x1b[34m\",\n end: \"\\x1b[0m\",\n },\n green: {\n start: \"\\x1b[32m\",\n end: \"\\x1b[0m\",\n },\n yellow: {\n start: \"\\x1b[33;1m\",\n end: \"\\x1b[0m\",\n },\n};\n\n/**\n * Wrap some text in a color for printing to the console.\n */\nexport const wrap = (color: ConsoleColors, text: string): string =>\n `${color.start}${text}${color.end}`;\n\nexport function printCheckpoint<Value>(\n step: number,\n channels: Record<string, BaseChannel<Value>>\n) {\n console.log(\n [\n `${wrap(COLORS_MAP.blue, \"[langgraph/checkpoint]\")}`,\n `Finishing step ${step}. Channel values:\\n`,\n `\\n${JSON.stringify(\n Object.fromEntries(_readChannels<Value>(channels)),\n null,\n 2\n )}`,\n ].join(\"\")\n );\n}\n\nexport function* _readChannels<Value>(\n channels: Record<string, BaseChannel<Value>>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): IterableIterator<[string, any]> {\n for (const [name, channel] of Object.entries(channels)) {\n try {\n yield [name, channel.get()];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n // Skip the channel if it's empty\n continue;\n } else {\n throw error; // Re-throw the error if it's not an EmptyChannelError\n }\n }\n }\n}\n\n/**\n * Build the user-meaningful metadata to forward on a task's stream payload.\n *\n * Drops langgraph's internal framework keys ({@link EXCLUDED_METADATA_KEYS}) —\n * which are redundant with the task's own fields and namespace — while keeping\n * keys like `lc_agent_name`, `ls_integration`, and any user-supplied metadata.\n * Filtered config tags are folded in under `tags`, mirroring the messages\n * stream handler. Returns `undefined` when there is nothing to forward.\n */\nfunction buildTaskMetadata(\n config: RunnableConfig | undefined\n): Record<string, unknown> | undefined {\n if (config == null) return undefined;\n const metadata: Record<string, unknown> = {};\n if (config.metadata != null) {\n for (const [key, value] of Object.entries(config.metadata)) {\n if (!EXCLUDED_METADATA_KEYS.has(key)) metadata[key] = value;\n }\n }\n const filteredTags = filterToUserTags(config.tags);\n if (filteredTags != null) metadata.tags = filteredTags;\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n}\n\nexport function* mapDebugTasks<N extends PropertyKey, C extends PropertyKey>(\n tasks: readonly PregelExecutableTask<N, C>[]\n) {\n for (const { id, name, input, config, triggers, writes } of tasks) {\n if (config?.tags?.includes(TAG_HIDDEN)) continue;\n\n const interrupts = writes\n .filter(([writeId, n]) => {\n return writeId === id && n === INTERRUPT;\n })\n .map(([, v]) => {\n return v;\n });\n const payload: {\n id: string;\n name: N;\n input: unknown;\n triggers: string[];\n interrupts: unknown[];\n metadata?: Record<string, unknown>;\n } = { id, name, input, triggers, interrupts };\n const metadata = buildTaskMetadata(config);\n if (metadata != null) payload.metadata = metadata;\n yield payload;\n }\n}\n\nfunction isMultipleChannelWrite(\n value: unknown\n): value is { $writes: unknown[] } {\n if (typeof value !== \"object\" || value === null) return false;\n return \"$writes\" in value && Array.isArray(value.$writes);\n}\n\nfunction mapTaskResultWrites(writes: PendingWrite<unknown>[]) {\n const result: Record<string, unknown> = {};\n\n for (const [channel, value] of writes) {\n const strChannel = String(channel);\n\n if (strChannel in result) {\n const channelWrites = isMultipleChannelWrite(result[strChannel])\n ? result[strChannel].$writes\n : [result[strChannel]];\n\n channelWrites.push(value);\n result[strChannel] = { $writes: channelWrites };\n } else {\n result[strChannel] = value;\n }\n }\n return result;\n}\n\nexport function* mapDebugTaskResults<\n N extends PropertyKey,\n C extends PropertyKey,\n>(\n tasks: readonly [PregelExecutableTask<N, C>, PendingWrite<C>[]][],\n streamChannels: PropertyKey | Array<PropertyKey>\n) {\n for (const [{ id, name, config }, writes] of tasks) {\n if (config?.tags?.includes(TAG_HIDDEN)) continue;\n yield {\n id,\n name,\n result: mapTaskResultWrites(\n writes.filter(([channel]) => {\n return Array.isArray(streamChannels)\n ? streamChannels.includes(channel)\n : channel === streamChannels;\n })\n ),\n interrupts: writes.filter((w) => w[0] === INTERRUPT).map((w) => w[1]),\n };\n }\n}\n\ntype ChannelKey = string | number | symbol;\n\nexport function* mapDebugCheckpoint<\n N extends PropertyKey,\n C extends PropertyKey,\n>(\n config: RunnableConfig,\n channels: Record<string, BaseChannel>,\n streamChannels: string | string[],\n metadata: CheckpointMetadata,\n tasks: readonly PregelExecutableTask<N, C>[],\n pendingWrites: CheckpointPendingWrite[],\n parentConfig: RunnableConfig | undefined,\n outputKeys: ChannelKey | ChannelKey[]\n) {\n function formatConfig(config: RunnableConfig) {\n // https://stackoverflow.com/a/78298178\n type CamelToSnake<\n T extends string,\n A extends string = \"\",\n > = T extends `${infer F}${infer R}`\n ? CamelToSnake<\n R,\n `${A}${F extends Lowercase<F> ? F : `_${Lowercase<F>}`}`\n >\n : A;\n\n // make sure the config is consistent with Python\n const pyConfig: Partial<\n Record<CamelToSnake<keyof RunnableConfig>, unknown>\n > = {};\n\n if (config.callbacks != null) pyConfig.callbacks = config.callbacks;\n if (config.configurable != null)\n pyConfig.configurable = config.configurable;\n if (config.maxConcurrency != null)\n pyConfig.max_concurrency = config.maxConcurrency;\n\n if (config.metadata != null) pyConfig.metadata = config.metadata;\n if (config.recursionLimit != null)\n pyConfig.recursion_limit = config.recursionLimit;\n if (config.runId != null) pyConfig.run_id = config.runId;\n if (config.runName != null) pyConfig.run_name = config.runName;\n if (config.tags != null) pyConfig.tags = config.tags;\n\n return pyConfig;\n }\n\n const parentNs = config.configurable?.checkpoint_ns;\n const taskStates: Record<string, RunnableConfig | StateSnapshot> = {};\n\n for (const task of tasks) {\n const candidates = task.subgraphs?.length ? task.subgraphs : [task.proc];\n if (!candidates.find(findSubgraphPregel)) continue;\n\n let taskNs = `${task.name as string}:${task.id}`;\n if (parentNs) taskNs = `${parentNs}|${taskNs}`;\n\n taskStates[task.id] = {\n configurable: {\n thread_id: config.configurable?.thread_id,\n checkpoint_ns: taskNs,\n },\n };\n }\n\n yield {\n config: formatConfig(config),\n values: readChannels(channels, streamChannels),\n metadata,\n next: tasks.map((task) => task.name),\n tasks: tasksWithWrites(tasks, pendingWrites, taskStates, outputKeys),\n parentConfig: parentConfig ? formatConfig(parentConfig) : undefined,\n };\n}\n\nexport function tasksWithWrites<N extends PropertyKey, C extends PropertyKey>(\n tasks: PregelTaskDescription[] | readonly PregelExecutableTask<N, C>[],\n pendingWrites: CheckpointPendingWrite[],\n states: Record<string, RunnableConfig | StateSnapshot> | undefined,\n outputKeys: ChannelKey[] | ChannelKey\n): PregelTaskDescription[] {\n return tasks.map((task): PregelTaskDescription => {\n const error = pendingWrites.find(\n ([id, n]) => id === task.id && n === ERROR\n )?.[2];\n\n const interrupts = pendingWrites\n .filter(([id, n]) => id === task.id && n === INTERRUPT)\n .map(([, , v]) => v) as Interrupt[];\n\n const result = (() => {\n if (error || interrupts.length || !pendingWrites.length) return undefined;\n\n const idx = pendingWrites.findIndex(\n ([tid, n]) => tid === task.id && n === RETURN\n );\n\n if (idx >= 0) return pendingWrites[idx][2];\n\n if (typeof outputKeys === \"string\") {\n return pendingWrites.find(\n ([tid, n]) => tid === task.id && n === outputKeys\n )?.[2];\n }\n\n if (Array.isArray(outputKeys)) {\n const results = pendingWrites\n .filter(([tid, n]) => tid === task.id && outputKeys.includes(n))\n .map(([, n, v]) => [n, v] as PendingWrite<C>);\n\n if (!results.length) return undefined;\n return mapTaskResultWrites(results);\n }\n\n return undefined;\n })();\n\n if (error) {\n return {\n id: task.id,\n name: task.name as string,\n path: task.path,\n error,\n interrupts,\n result,\n };\n }\n\n const taskState = states?.[task.id];\n return {\n id: task.id,\n name: task.name as string,\n path: task.path,\n interrupts,\n ...(taskState !== undefined ? { state: taskState } : {}),\n result,\n };\n });\n}\n\nexport function printStepCheckpoint(\n step: number,\n channels: Record<string, BaseChannel<unknown>>,\n whitelist: string[]\n): void {\n console.log(\n [\n `${wrap(COLORS_MAP.blue, `[${step}:checkpoint]`)}`,\n `\\x1b[1m State at the end of step ${step}:\\x1b[0m\\n`,\n JSON.stringify(readChannels(channels, whitelist), null, 2),\n ].join(\"\")\n );\n}\n\nexport function printStepTasks<N extends PropertyKey, C extends PropertyKey>(\n step: number,\n nextTasks: readonly PregelExecutableTask<N, C>[]\n): void {\n const nTasks = nextTasks.length;\n console.log(\n [\n `${wrap(COLORS_MAP.blue, `[${step}:tasks]`)}`,\n `\\x1b[1m Starting step ${step} with ${nTasks} task${\n nTasks === 1 ? \"\" : \"s\"\n }:\\x1b[0m\\n`,\n nextTasks\n .map(\n (task) =>\n `- ${wrap(COLORS_MAP.green, String(task.name))} -> ${JSON.stringify(\n task.input,\n null,\n 2\n )}`\n )\n .join(\"\\n\"),\n ].join(\"\")\n );\n}\n\nexport function printStepWrites(\n step: number,\n writes: PendingWrite[],\n whitelist: string[]\n): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const byChannel: Record<string, any[]> = {};\n\n for (const [channel, value] of writes) {\n if (whitelist.includes(channel)) {\n if (!byChannel[channel]) {\n byChannel[channel] = [];\n }\n byChannel[channel].push(value);\n }\n }\n\n console.log(\n [\n `${wrap(COLORS_MAP.blue, `[${step}:writes]`)}`,\n `\\x1b[1m Finished step ${step} with writes to ${\n Object.keys(byChannel).length\n } channel${Object.keys(byChannel).length !== 1 ? \"s\" : \"\"}:\\x1b[0m\\n`,\n Object.entries(byChannel)\n .map(\n ([name, vals]) =>\n `- ${wrap(COLORS_MAP.yellow, name)} -> ${vals\n .map((v) => JSON.stringify(v))\n .join(\", \")}`\n )\n .join(\"\\n\"),\n ].join(\"\")\n );\n}\n"],"mappings":"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"}