mirror of
https://github.com/langchain-ai/langgraphjs.git
synced 2026-07-25 21:26:05 -04:00
fix(langgraph): fix invalid safe serialization of RunnableConfig for RemoteGraph (#1473)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@langchain/langgraph": patch
|
||||
---
|
||||
|
||||
fix(langgraph): RemotePregel serialization fix
|
||||
@@ -219,24 +219,25 @@ export class RemoteGraph<
|
||||
"checkpoint_ns",
|
||||
]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const sanitizeObj = (obj: any): any => {
|
||||
// Remove non-JSON serializable fields from the given object
|
||||
if (obj && typeof obj === "object") {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((v) => sanitizeObj(v));
|
||||
} else {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).map(([k, v]) => [k, sanitizeObj(v)])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const sanitizeObj = <T>(obj: T): T => {
|
||||
try {
|
||||
// This will only throw if we're trying to serialize a circular reference
|
||||
// or trying to serialize a BigInt...
|
||||
JSON.stringify(obj);
|
||||
return obj;
|
||||
} catch {
|
||||
return null;
|
||||
const seen = new WeakSet();
|
||||
return JSON.parse(
|
||||
JSON.stringify(obj, (_, value) => {
|
||||
if (typeof value === "object" && value != null) {
|
||||
if (seen.has(value)) return "[Circular]";
|
||||
seen.add(value);
|
||||
}
|
||||
|
||||
if (typeof value === "bigint") return value.toString();
|
||||
return value;
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -577,4 +577,64 @@ describe("RemoteGraph", () => {
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test("handle circular references", async () => {
|
||||
const client = new Client({});
|
||||
const streamSpy = vi
|
||||
.spyOn((client as any).runs, "stream")
|
||||
.mockImplementation(async function* () {
|
||||
yield {
|
||||
event: "values",
|
||||
data: { messages: [{ type: "human", content: "world" }] },
|
||||
};
|
||||
});
|
||||
|
||||
const remotePregel = new RemoteGraph({ client, graphId: "test_graph_id" });
|
||||
|
||||
const config: any = {
|
||||
configurable: { thread_id: "thread_1", bigint: 123n },
|
||||
metadata: { source: "test" },
|
||||
tags: [],
|
||||
};
|
||||
config.configurable.circular = config;
|
||||
config.metadata.circular = config;
|
||||
config.tags.push(config);
|
||||
|
||||
const result = await remotePregel.invoke({}, config);
|
||||
expect(result).toEqual({ messages: [{ type: "human", content: "world" }] });
|
||||
|
||||
expect(streamSpy).toHaveBeenCalledWith(
|
||||
"thread_1",
|
||||
"test_graph_id",
|
||||
expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
configurable: {
|
||||
circular: "[Circular]",
|
||||
bigint: "123",
|
||||
thread_id: "thread_1",
|
||||
},
|
||||
metadata: {
|
||||
source: "test",
|
||||
circular: "[Circular]",
|
||||
thread_id: "thread_1",
|
||||
},
|
||||
tags: [
|
||||
{
|
||||
configurable: {
|
||||
circular: "[Circular]",
|
||||
bigint: "123",
|
||||
thread_id: "thread_1",
|
||||
},
|
||||
metadata: {
|
||||
source: "test",
|
||||
circular: "[Circular]",
|
||||
thread_id: "thread_1",
|
||||
},
|
||||
tags: ["[Circular]"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user