-
Notifications
You must be signed in to change notification settings - Fork 452
Implement serde, getTuple(), and list() in BaseCheckpointSaver
#119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
d5de77f
c186fb3
7ed84ef
588de4b
e840069
c0cf1a5
6cd62c7
2d0a723
a4c7ec7
4a6fabd
d2abdfc
c49f256
1cbba0f
e473fa3
78613be
348572c
df81bb5
83cd32b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,24 @@ export interface Checkpoint { | |
| versionsSeen: Record<string, Record<string, number>>; | ||
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| export function deepCopy(obj: any): any { | ||
| if (typeof obj !== "object" || obj === null) { | ||
| return obj; | ||
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| const newObj: any = Array.isArray(obj) ? [] : {}; | ||
|
|
||
| for (const key in obj) { | ||
| if (Object.prototype.hasOwnProperty.call(obj, key)) { | ||
| newObj[key] = deepCopy(obj[key]); | ||
| } | ||
| } | ||
|
|
||
| return newObj; | ||
| } | ||
|
|
||
| export function emptyCheckpoint(): Checkpoint { | ||
| return { | ||
| v: 1, | ||
|
|
@@ -56,7 +74,7 @@ export function copyCheckpoint(checkpoint: Checkpoint): Checkpoint { | |
| ts: checkpoint.ts, | ||
| channelValues: { ...checkpoint.channelValues }, | ||
| channelVersions: { ...checkpoint.channelVersions }, | ||
| versionsSeen: { ...checkpoint.versionsSeen }, | ||
| versionsSeen: deepCopy(checkpoint.versionsSeen), | ||
| }; | ||
| } | ||
|
|
||
|
|
@@ -65,14 +83,67 @@ export const enum CheckpointAt { | |
| END_OF_RUN = "end_of_run", | ||
| } | ||
|
|
||
| export interface CheckpointTuple { | ||
| config: RunnableConfig; | ||
| checkpoint: Checkpoint; | ||
| parentConfig?: RunnableConfig; | ||
| } | ||
|
|
||
| const CheckpointThreadId: ConfigurableFieldSpec = { | ||
| id: "threadId", | ||
| annotation: typeof "", | ||
| name: "Thread ID", | ||
| description: null, | ||
| default: "", | ||
| isShared: true, | ||
| dependencies: null, | ||
| }; | ||
|
|
||
| const CheckpointThreadTs: ConfigurableFieldSpec = { | ||
| id: "threadTs", | ||
| annotation: typeof "", | ||
| name: "Thread Timestamp", | ||
| description: | ||
| "Pass to fetch a past checkpoint. If None, fetches the latest checkpoint.", | ||
| default: null, | ||
| isShared: true, | ||
| dependencies: null, | ||
| }; | ||
|
|
||
| export interface SerializerProtocol { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| dumps(obj: any): any; | ||
|
||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| loads(data: any): any; | ||
| } | ||
|
|
||
| export abstract class BaseCheckpointSaver { | ||
| at: CheckpointAt = CheckpointAt.END_OF_RUN; | ||
| at: CheckpointAt = CheckpointAt.END_OF_STEP; | ||
|
|
||
| serde: SerializerProtocol; | ||
|
|
||
| constructor(serde?: SerializerProtocol, at?: CheckpointAt) { | ||
| this.serde = serde || this.serde; | ||
| this.at = at || this.at; | ||
| } | ||
|
|
||
| get configSpecs(): Array<ConfigurableFieldSpec> { | ||
| return []; | ||
| return [CheckpointThreadId, CheckpointThreadTs]; | ||
| } | ||
|
|
||
| abstract get(config: RunnableConfig): Checkpoint | undefined; | ||
| async get(config: RunnableConfig): Promise<Checkpoint | undefined> { | ||
| const value = await this.getTuple(config); | ||
| return value ? value.checkpoint : undefined; | ||
| } | ||
|
|
||
| abstract getTuple( | ||
| config: RunnableConfig | ||
| ): Promise<CheckpointTuple | undefined>; | ||
|
|
||
| abstract list(config: RunnableConfig): AsyncGenerator<CheckpointTuple>; | ||
|
|
||
| abstract put(config: RunnableConfig, checkpoint: Checkpoint): void; | ||
| abstract put( | ||
| config: RunnableConfig, | ||
| checkpoint: Checkpoint | ||
| ): Promise<RunnableConfig>; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nuno Campos (@nfcampos), general question: The JS implementation serializes
threadIdandthreadTsincamelCase(as opposed tosnake_casein 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep would be nice to use same keys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll make the changes in this PR.