@langchain/langgraph-checkpoint
Version:
Library with base interfaces for LangGraph checkpoint savers.
147 lines • 4.7 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { BaseStore, } from "./base.js";
/**
* Extracts and returns the underlying store from an `AsyncBatchedStore`,
* or returns the input if it is not an `AsyncBatchedStore`.
*/
const extractStore = (input) => {
if ("lg_name" in input && input.lg_name === "AsyncBatchedStore") {
// @ts-expect-error is a protected property
return input.store;
}
return input;
};
export class AsyncBatchedStore extends BaseStore {
constructor(store) {
super();
Object.defineProperty(this, "lg_name", {
enumerable: true,
configurable: true,
writable: true,
value: "AsyncBatchedStore"
});
Object.defineProperty(this, "store", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "queue", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "nextKey", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "running", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "processingTask", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
this.store = extractStore(store);
}
get isRunning() {
return this.running;
}
/**
* @ignore
* Batch is not implemented here as we're only extending `BaseStore`
* to allow it to be passed where `BaseStore` is expected, and implement
* the convenience methods (get, search, put, delete).
*/
async batch(_operations) {
throw new Error("The `batch` method is not implemented on `AsyncBatchedStore`." +
"\n Instead, it calls the `batch` method on the wrapped store." +
"\n If you are seeing this error, something is wrong.");
}
async get(namespace, key) {
return this.enqueueOperation({ namespace, key });
}
async search(namespacePrefix, options) {
const { filter, limit = 10, offset = 0, query } = options || {};
return this.enqueueOperation({
namespacePrefix,
filter,
limit,
offset,
query,
});
}
async put(namespace, key, value) {
return this.enqueueOperation({ namespace, key, value });
}
async delete(namespace, key) {
return this.enqueueOperation({
namespace,
key,
value: null,
});
}
start() {
if (!this.running) {
this.running = true;
this.processingTask = this.processBatchQueue();
}
}
async stop() {
this.running = false;
if (this.processingTask) {
await this.processingTask;
}
}
enqueueOperation(operation) {
return new Promise((resolve, reject) => {
const key = this.nextKey;
this.nextKey += 1;
this.queue.set(key, { operation, resolve, reject });
});
}
async processBatchQueue() {
while (this.running) {
await new Promise((resolve) => {
setTimeout(resolve, 0);
});
if (this.queue.size === 0)
continue;
const batch = new Map(this.queue);
this.queue.clear();
try {
const operations = Array.from(batch.values()).map(({ operation }) => operation);
const results = await this.store.batch(operations);
batch.forEach(({ resolve }, key) => {
const index = Array.from(batch.keys()).indexOf(key);
resolve(results[index]);
});
}
catch (e) {
batch.forEach(({ reject }) => {
reject(e);
});
}
}
}
// AsyncBatchedStore is internal and gets passed as args into traced tasks
// some BaseStores contain circular references so just serialize without it
// as this causes warnings when tracing with LangSmith.
toJSON() {
return {
queue: this.queue,
nextKey: this.nextKey,
running: this.running,
store: "[LangGraphStore]",
};
}
}
//# sourceMappingURL=batch.js.map