Implement serde, getTuple(), and list() in BaseCheckpointSaver#119
Conversation
There was a problem hiding this comment.
This file is named checkpoints.test.ts instead of checkpoint.test.ts because the jest --testPathIgnorePatterns=\\.int\\.test.ts config in package.json is ignoring the later file name for some reason. The regex is correct, but there seems to be a bug with jest.
I'm not sure what the best solution is. I tried updating the regex to require at least one character before the first dot (.), but I couldn't get jest to pick up any files.
| } | ||
|
|
||
| const CheckpointThreadId: ConfigurableFieldSpec = { | ||
| id: "threadId", |
There was a problem hiding this comment.
Nuno Campos (@nfcampos), general question: The JS implementation serializes threadId and threadTs in camelCase (as opposed to snake_case in Python). Is there any value in making them both one way or the other?
Hypothetical scenario: Python graph checkpoints state, pauses execution, and JS graph (or some other runtime) resumes execution.
There was a problem hiding this comment.
yep would be nice to use same keys
There was a problem hiding this comment.
I'll make the changes in this PR.
langgraph/src/checkpoint/base.ts
Outdated
| } | ||
|
|
||
| export abstract class BaseCheckpointSaver { | ||
| at: CheckpointAt = CheckpointAt.END_OF_RUN; |
There was a problem hiding this comment.
Change this to CheckpointAt.END_OF_STEP.
langgraph/src/checkpoint/base.ts
Outdated
|
|
||
| export interface SerializerProtocol { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| dumps(obj: any): any; |
There was a problem hiding this comment.
Not sure what the right input/output types are for dumps() and loads().
There was a problem hiding this comment.
I think in JS the most common would be unknown -> string -> unknown
There was a problem hiding this comment.
Let me know if you like this approach better: 348572c (commit)
I implemented generics on the BaseCheckpointSaver class and the SerializerProtocol interface. This ensures that a type incompatible serializer cannot be passed to the checkpoint saver constructor. For example, the SqliteSaver requires the checkpoint to be serialized to a string. A serializer that doesn't implement this correctly will raise an error. It's also nice to help developers implement dumps() and loads() with the correct types.
That being said, I'm not sure how much value this provides.
langgraph/src/checkpoint/sqlite.ts
Outdated
| ) { | ||
| super(serde, at); | ||
| this.db = new Database(connStringOrLocalPath); | ||
| this.setup().catch((error) => |
There was a problem hiding this comment.
isn't there a race condition here? you could end up calling one of the methods before setup finishes
There was a problem hiding this comment.
Yes, you're right. Let me fix.
I reproduced the race condition by adding a sleep statement.
private async setup(): Promise<void> {
// sleep for 3 seconds
await new Promise((resolve) => setTimeout(resolve, 3000));
...
}The unit tests failed.
| private async setup(): Promise<void> { | ||
| try { | ||
| this.db.exec(` | ||
| CREATE TABLE IF NOT EXISTS checkpoints ( |
There was a problem hiding this comment.
prob worth enabling wal mode? I know I haven't done that in py, prob should there too
There was a problem hiding this comment.
I know I haven't done that in py, prob should there too
Added this to my To Do list for later.
langgraph/src/checkpoint/base.ts
Outdated
| loads(data: L): D; | ||
| } | ||
|
|
||
| export abstract class BaseCheckpointSaver<D, L> { |
There was a problem hiding this comment.
D here should always be Checkpoint
There was a problem hiding this comment.
Makes sense. I removed D and hardcoded Checkpoint in the internal implementation of BaseCheckpointSaver.
Summary
To get checkpointing functionality up to parity with LangGraph's Python implementation, the
BaseCheckpointSaverclass needs to be updated to defineserde,getTuple()andlist(). Downstream dependencies (e.g.MemorySaver) are updated accordingly.Implementation
deepCopy()to/checkpoint/base.ts.BaseCheckpointSaver: Defineserdeattribute. DefinegetTuple()andlist()methods. Update methods to returnPromise.MemorySaver: Updatestorageattribute type. Update method implementations.SqliteSaver.To Do
serdeinMemorySaver.getTuple()andlist().camel_case.Add changes forSqliteSaverfrom this PR: Add FewShotExamples managed value langgraph#332