From 6a33f437bc7cf52887cbb25f2f9a1aa06694e4c6 Mon Sep 17 00:00:00 2001 From: Tat Dat Duong Date: Tue, 23 Sep 2025 23:12:03 +0200 Subject: [PATCH 1/2] chore(langgraph): remove deprecated `getGraph` in favour of `getGraphAsync` --- libs/langgraph/src/graph/graph.ts | 217 ++++------------------ libs/langgraph/src/pregel/index.ts | 33 +--- libs/langgraph/src/pregel/remote.ts | 16 -- libs/langgraph/src/pregel/types.ts | 7 - libs/langgraph/src/tests/diagrams.test.ts | 6 +- libs/langgraph/src/tests/pregel.test.ts | 6 +- 6 files changed, 45 insertions(+), 240 deletions(-) diff --git a/libs/langgraph/src/graph/graph.ts b/libs/langgraph/src/graph/graph.ts index 098b64531..d55d7d784 100644 --- a/libs/langgraph/src/graph/graph.ts +++ b/libs/langgraph/src/graph/graph.ts @@ -33,11 +33,7 @@ import { START, TAG_HIDDEN, } from "../constants.js"; -import { - gatherIterator, - gatherIteratorSync, - RunnableCallable, -} from "../utils.js"; +import { gatherIterator, RunnableCallable } from "../utils.js"; import { InvalidUpdateError, NodeInterrupt, @@ -755,13 +751,32 @@ export class CompiledGraph< } if (xray) { const newXrayValue = typeof xray === "number" ? xray - 1 : xray; - const drawableSubgraph = - subgraphs[key] !== undefined - ? await subgraphs[key].getGraphAsync({ - ...config, - xray: newXrayValue, - }) + + const hasGraphAsync = ( + node: unknown + ): node is { + getGraphAsync: (config: RunnableConfig) => Promise; + } => { + return ( + typeof node === "object" && + node !== null && + "getGraphAsync" in node && + typeof node.getGraphAsync === "function" + ); + }; + + const drawableSubgraph = await (async () => { + if (subgraphs[key] !== undefined) { + return subgraphs[key].getGraphAsync({ + ...config, + xray: newXrayValue, + }); + } + + return hasGraphAsync(node) + ? node.getGraphAsync(config) : node.getGraph(config); + })(); drawableSubgraph.trimFirstNode(); drawableSubgraph.trimLastNode(); @@ -890,182 +905,12 @@ export class CompiledGraph< /** * Returns a drawable representation of the computation graph. * - * @deprecated Use getGraphAsync instead. The async method will be the default in the next minor core release. + * @deprecated Use getGraphAsync instead. */ - override getGraph( - config?: RunnableConfig & { xray?: boolean | number } - ): DrawableGraph { - const xray = config?.xray; - const graph = new DrawableGraph(); - const startNodes: Record = { - [START]: graph.addNode( - { - schema: z.any(), - }, - START - ), - }; - const endNodes: Record = {}; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let subgraphs: Record> = {}; - if (xray) { - subgraphs = Object.fromEntries( - gatherIteratorSync(this.getSubgraphs()).filter( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (x): x is [string, CompiledGraph] => isCompiledGraph(x[1]) - ) - ); - } - - function addEdge( - start: string, - end: string, - label?: string, - conditional = false - ) { - if (end === END && endNodes[END] === undefined) { - endNodes[END] = graph.addNode({ schema: z.any() }, END); - } - return graph.addEdge( - startNodes[start], - endNodes[end], - label !== end ? label : undefined, - conditional - ); - } - - for (const [key, nodeSpec] of Object.entries(this.builder.nodes) as [ - N, - NodeSpec - ][]) { - const displayKey = _escapeMermaidKeywords(key); - const node = nodeSpec.runnable; - const metadata = nodeSpec.metadata ?? {}; - if ( - this.interruptBefore?.includes(key) && - this.interruptAfter?.includes(key) - ) { - metadata.__interrupt = "before,after"; - } else if (this.interruptBefore?.includes(key)) { - metadata.__interrupt = "before"; - } else if (this.interruptAfter?.includes(key)) { - metadata.__interrupt = "after"; - } - if (xray) { - const newXrayValue = typeof xray === "number" ? xray - 1 : xray; - const drawableSubgraph = - subgraphs[key] !== undefined - ? subgraphs[key].getGraph({ - ...config, - xray: newXrayValue, - }) - : node.getGraph(config); - drawableSubgraph.trimFirstNode(); - drawableSubgraph.trimLastNode(); - if (Object.keys(drawableSubgraph.nodes).length > 1) { - const [e, s] = graph.extend(drawableSubgraph, displayKey); - if (e === undefined) { - throw new Error( - `Could not extend subgraph "${key}" due to missing entrypoint.` - ); - } - - // TODO: Remove default name once we stop supporting core 0.2.0 - // eslint-disable-next-line no-inner-declarations - function _isRunnableInterface( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - thing: any - ): thing is RunnableInterface { - return thing ? thing.lc_runnable : false; - } - // eslint-disable-next-line no-inner-declarations - function _nodeDataStr( - id: string | undefined, - data: RunnableInterface | RunnableIOSchema - ): string { - if (id !== undefined && !isUuid(id)) { - return id; - } else if (_isRunnableInterface(data)) { - try { - let dataStr = data.getName(); - dataStr = dataStr.startsWith("Runnable") - ? dataStr.slice("Runnable".length) - : dataStr; - return dataStr; - } catch (error) { - return data.getName(); - } - } else { - return data.name ?? "UnknownSchema"; - } - } - // TODO: Remove casts when we stop supporting core 0.2.0 - if (s !== undefined) { - startNodes[displayKey] = { - name: _nodeDataStr(s.id, s.data), - ...s, - } as DrawableGraphNode; - } - endNodes[displayKey] = { - name: _nodeDataStr(e.id, e.data), - ...e, - } as DrawableGraphNode; - } else { - // TODO: Remove when we stop supporting core 0.2.0 - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const newNode = graph.addNode(node, displayKey, metadata); - startNodes[displayKey] = newNode; - endNodes[displayKey] = newNode; - } - } else { - // TODO: Remove when we stop supporting core 0.2.0 - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const newNode = graph.addNode(node, displayKey, metadata); - startNodes[displayKey] = newNode; - endNodes[displayKey] = newNode; - } - } - const sortedEdges = [...this.builder.allEdges].sort(([a], [b]) => { - if (a < b) { - return -1; - } else if (b > a) { - return 1; - } else { - return 0; - } - }); - for (const [start, end] of sortedEdges) { - addEdge(_escapeMermaidKeywords(start), _escapeMermaidKeywords(end)); - } - for (const [start, branches] of Object.entries(this.builder.branches)) { - const defaultEnds: Record = { - ...Object.fromEntries( - Object.keys(this.builder.nodes) - .filter((k) => k !== start) - .map((k) => [_escapeMermaidKeywords(k), _escapeMermaidKeywords(k)]) - ), - [END]: END, - }; - for (const branch of Object.values(branches)) { - let ends; - if (branch.ends !== undefined) { - ends = branch.ends; - } else { - ends = defaultEnds; - } - for (const [label, end] of Object.entries(ends)) { - addEdge( - _escapeMermaidKeywords(start), - _escapeMermaidKeywords(end), - label, - true - ); - } - } - } - return graph; + override getGraph(): DrawableGraph { + throw new Error( + `The synchronous "getGraph" is not supported for this graph. Call "getGraphAsync" instead.` + ); } } diff --git a/libs/langgraph/src/pregel/index.ts b/libs/langgraph/src/pregel/index.ts index fd5f0ae36..dde3895f7 100644 --- a/libs/langgraph/src/pregel/index.ts +++ b/libs/langgraph/src/pregel/index.ts @@ -666,19 +666,18 @@ export class Pregel< } /** - * Gets all subgraphs within this graph. + * Gets all subgraphs within this graph asynchronously. * A subgraph is a Pregel instance that is nested within a node of this graph. * - * @deprecated Use getSubgraphsAsync instead. The async method will become the default in the next minor release. * @param namespace - Optional namespace to filter subgraphs * @param recurse - Whether to recursively get subgraphs of subgraphs - * @returns Generator yielding tuples of [name, subgraph] + * @returns AsyncGenerator yielding tuples of [name, subgraph] */ - *getSubgraphs( + async *getSubgraphsAsync( namespace?: string, recurse?: boolean // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): Generator<[string, Pregel]> { + ): AsyncGenerator<[string, Pregel]> { for (const [name, node] of Object.entries(this.nodes)) { // filter by prefix if (namespace !== undefined) { @@ -710,10 +709,10 @@ export class Pregel< if (namespace !== undefined) { newNamespace = namespace.slice(name.length + 1); } - for (const [subgraphName, subgraph] of graph.getSubgraphs( - newNamespace, - recurse - )) { + for await (const [ + subgraphName, + subgraph, + ] of graph.getSubgraphsAsync(newNamespace, recurse)) { yield [ `${name}${CHECKPOINT_NAMESPACE_SEPARATOR}${subgraphName}`, subgraph, @@ -725,22 +724,6 @@ export class Pregel< } } - /** - * Gets all subgraphs within this graph asynchronously. - * A subgraph is a Pregel instance that is nested within a node of this graph. - * - * @param namespace - Optional namespace to filter subgraphs - * @param recurse - Whether to recursively get subgraphs of subgraphs - * @returns AsyncGenerator yielding tuples of [name, subgraph] - */ - async *getSubgraphsAsync( - namespace?: string, - recurse?: boolean - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): AsyncGenerator<[string, Pregel]> { - yield* this.getSubgraphs(namespace, recurse); - } - /** * Prepares a state snapshot from saved checkpoint data. * This is an internal method used by getState and getStateHistory. diff --git a/libs/langgraph/src/pregel/remote.ts b/libs/langgraph/src/pregel/remote.ts index e31207367..13f8ff892 100644 --- a/libs/langgraph/src/pregel/remote.ts +++ b/libs/langgraph/src/pregel/remote.ts @@ -574,15 +574,6 @@ export class RemoteGraph< return this._createStateSnapshot(state); } - /** @deprecated Use getGraphAsync instead. The async method will become the default in the next minor release. */ - override getGraph( - _?: RunnableConfig & { xray?: boolean | number } - ): DrawableGraph { - throw new Error( - `The synchronous "getGraph" is not supported for this graph. Call "getGraphAsync" instead.` - ); - } - /** * Returns a drawable representation of the computation graph. */ @@ -596,13 +587,6 @@ export class RemoteGraph< }); } - /** @deprecated Use getSubgraphsAsync instead. The async method will become the default in the next minor release. */ - getSubgraphs(): Generator<[string, PregelInterface]> { - throw new Error( - `The synchronous "getSubgraphs" method is not supported for this graph. Call "getSubgraphsAsync" instead.` - ); - } - async *getSubgraphsAsync( namespace?: string, recurse = false diff --git a/libs/langgraph/src/pregel/types.ts b/libs/langgraph/src/pregel/types.ts index 38d839e7b..08128f829 100644 --- a/libs/langgraph/src/pregel/types.ts +++ b/libs/langgraph/src/pregel/types.ts @@ -359,13 +359,6 @@ export interface PregelInterface< config: RunnableConfig & { xray?: boolean | number } ): Promise; - /** @deprecated Use getSubgraphsAsync instead. The async method will become the default in the next minor release. */ - getSubgraphs( - namespace?: string, - recurse?: boolean - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): Generator<[string, PregelInterface]>; - getSubgraphsAsync( namespace?: string, recurse?: boolean diff --git a/libs/langgraph/src/tests/diagrams.test.ts b/libs/langgraph/src/tests/diagrams.test.ts index df24161a6..c44a47365 100644 --- a/libs/langgraph/src/tests/diagrams.test.ts +++ b/libs/langgraph/src/tests/diagrams.test.ts @@ -12,7 +12,7 @@ test("prebuilt agent", async () => { const app = createReactAgent({ llm: model, tools }); - const graph = app.getGraph(); + const graph = await app.getGraphAsync(); const mermaid = graph.drawMermaid(); expect(mermaid).toEqual(`%%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -40,7 +40,7 @@ test("graph with multiple sinks", async () => { .addConditionalEdges("inner1", async () => "inner2", ["inner2", "inner3"]) .compile(); - const graph = app.getGraph(); + const graph = await app.getGraphAsync(); const mermaid = graph.drawMermaid(); expect(mermaid).toEqual(`%%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; @@ -77,7 +77,7 @@ test("graph with subgraphs", async () => { .addConditionalEdges("starter", async () => "final", ["inner", "final"]) .compile({ interruptBefore: ["starter"] }); - const graph = app.getGraph({ xray: true }); + const graph = await app.getGraphAsync({ xray: true }); const mermaid = graph.drawMermaid(); expect(mermaid).toEqual(`%%{init: {'flowchart': {'curve': 'linear'}}}%% graph TD; diff --git a/libs/langgraph/src/tests/pregel.test.ts b/libs/langgraph/src/tests/pregel.test.ts index e55322ef6..aede69f0e 100644 --- a/libs/langgraph/src/tests/pregel.test.ts +++ b/libs/langgraph/src/tests/pregel.test.ts @@ -5074,7 +5074,7 @@ graph TD; .addEdge("action", "agent") .compile(); - expect(app.getGraph().toJSON()).toMatchObject({ + expect((await app.getGraphAsync()).toJSON()).toMatchObject({ nodes: expect.arrayContaining([ { id: "__start__", @@ -5237,7 +5237,7 @@ graph TD; .addEdge("action", "agent") .compile(); - expect(app.getGraph().toJSON()).toMatchObject({ + expect((await app.getGraphAsync()).toJSON()).toMatchObject({ nodes: expect.arrayContaining([ expect.objectContaining({ id: "__start__", type: "schema" }), expect.objectContaining({ id: "__end__", type: "schema" }), @@ -5940,7 +5940,7 @@ graph TD; const tool = toolBuilder.compile(); - expect(tool.getGraph().toJSON()).toMatchObject({ + expect((await tool.getGraphAsync()).toJSON()).toMatchObject({ nodes: expect.arrayContaining([ expect.objectContaining({ id: "__start__", type: "schema" }), { From 02cec8f00aff3bd7c857cb8dc5fdf91e284b90e1 Mon Sep 17 00:00:00 2001 From: Tat Dat Duong Date: Tue, 23 Sep 2025 23:13:34 +0200 Subject: [PATCH 2/2] Add changeset, update changeset config --- .changeset/config.json | 3 +-- .changeset/thick-wings-open.md | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/thick-wings-open.md diff --git a/.changeset/config.json b/.changeset/config.json index e94b80fc8..e49384b3b 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -15,8 +15,7 @@ "updateInternalDependencies": "patch", "ignore": [ "@langchain/langgraph-sdk-validation", - "@langchain/langgraph-benchmark", - "examples" + "@langchain/langgraph-benchmark" ], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "onlyUpdatePeerDependentsWhenOutOfRange": true diff --git a/.changeset/thick-wings-open.md b/.changeset/thick-wings-open.md new file mode 100644 index 000000000..9f40e41fc --- /dev/null +++ b/.changeset/thick-wings-open.md @@ -0,0 +1,5 @@ +--- +"@langchain/langgraph": minor +--- + +Remove deprecated `getGraph` method. Use `getGraphAsync` instead.