diff --git a/libs/checkpoint-sqlite/src/index.ts b/libs/checkpoint-sqlite/src/index.ts index a75542ed7..b5f521126 100644 --- a/libs/checkpoint-sqlite/src/index.ts +++ b/libs/checkpoint-sqlite/src/index.ts @@ -401,8 +401,14 @@ CREATE TABLE IF NOT EXISTS writes ( throw new Error("Empty configuration supplied."); } - if (!config.configurable?.thread_id) { - throw new Error("Missing thread_id field in config.configurable."); + const thread_id = config.configurable?.thread_id; + const checkpoint_ns = config.configurable?.checkpoint_ns ?? ""; + const parent_checkpoint_id = config.configurable?.checkpoint_id; + + if (!thread_id) { + throw new Error( + `Missing "thread_id" field in passed "config.configurable".` + ); } const preparedCheckpoint: Partial = copyCheckpoint(checkpoint); @@ -417,10 +423,10 @@ CREATE TABLE IF NOT EXISTS writes ( ); } const row = [ - config.configurable?.thread_id?.toString(), - config.configurable?.checkpoint_ns, + thread_id, + checkpoint_ns, checkpoint.id, - config.configurable?.checkpoint_id, + parent_checkpoint_id, type1, serializedCheckpoint, serializedMetadata, @@ -434,8 +440,8 @@ CREATE TABLE IF NOT EXISTS writes ( return { configurable: { - thread_id: config.configurable?.thread_id, - checkpoint_ns: config.configurable?.checkpoint_ns, + thread_id, + checkpoint_ns, checkpoint_id: checkpoint.id, }, }; diff --git a/libs/checkpoint-sqlite/src/tests/checkpoints.test.ts b/libs/checkpoint-sqlite/src/tests/checkpoints.test.ts index 3cc088303..c5498b54d 100644 --- a/libs/checkpoint-sqlite/src/tests/checkpoints.test.ts +++ b/libs/checkpoint-sqlite/src/tests/checkpoints.test.ts @@ -60,6 +60,7 @@ describe("SqliteSaver", () => { expect(runnableConfig).toEqual({ configurable: { thread_id: "1", + checkpoint_ns: "", checkpoint_id: checkpoint1.id, }, }); diff --git a/libs/checkpoint-validation/src/spec/put.ts b/libs/checkpoint-validation/src/spec/put.ts index d4a731efa..71c7d2039 100644 --- a/libs/checkpoint-validation/src/spec/put.ts +++ b/libs/checkpoint-validation/src/spec/put.ts @@ -177,37 +177,29 @@ export function putTests( }); }); - it_skipForSomeModules(initializer.checkpointerName, { - // TODO: SqliteSaver stores with undefined namespace instead of empty namespace - // see: https://github.com/langchain-ai/langgraphjs/issues/592 - "@langchain/langgraph-checkpoint-sqlite": - "TODO: SqliteSaver stores config with no checkpoint_ns instead of default namespace", - })( - "should default to empty namespace if the checkpoint namespace is missing from config.configurable", - async () => { - const missingNamespaceConfig: RunnableConfig = { - configurable: { thread_id }, - }; - - const { checkpoint, metadata } = initialCheckpointTuple({ - thread_id, - checkpoint_id: checkpoint_id1, - checkpoint_ns: "", - }); + it("should default to empty namespace if the checkpoint namespace is missing from config.configurable", async () => { + const missingNamespaceConfig: RunnableConfig = { + configurable: { thread_id }, + }; + + const { checkpoint, metadata } = initialCheckpointTuple({ + thread_id, + checkpoint_id: checkpoint_id1, + checkpoint_ns: "", + }); - const returnedConfig = await checkpointer.put( - missingNamespaceConfig, - checkpoint, - metadata!, - {} - ); - - expect(returnedConfig).not.toBeUndefined(); - expect(returnedConfig?.configurable).not.toBeUndefined(); - expect(returnedConfig?.configurable?.checkpoint_ns).not.toBeUndefined(); - expect(returnedConfig?.configurable?.checkpoint_ns).toEqual(""); - } - ); + const returnedConfig = await checkpointer.put( + missingNamespaceConfig, + checkpoint, + metadata!, + {} + ); + + expect(returnedConfig).not.toBeUndefined(); + expect(returnedConfig?.configurable).not.toBeUndefined(); + expect(returnedConfig?.configurable?.checkpoint_ns).not.toBeUndefined(); + expect(returnedConfig?.configurable?.checkpoint_ns).toEqual(""); + }); it_skipForSomeModules(initializer.checkpointerName, { // TODO: all of the checkpointers below store full channel_values on every put, rather than storing deltas