Skip to content
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

Add Supabase Checkpointer #641

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions libs/checkpoint-supabase/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
index.cjs
index.js
index.d.ts
index.d.cts
node_modules
dist
.yarn
110 changes: 110 additions & 0 deletions libs/checkpoint-supabase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# @langchain/langgraph-checkpoint-supabase

Implementation of a [LangGraph.js](https://github.com/langchain-ai/langgraphjs) CheckpointSaver that uses the Supabase JS SDK.

## Setup

Create the following tables in your Supabase database, you can change the table names if required when also setting the `checkPointTable` and `writeTable` options of the `SupabaseSaver` class.

> [!CAUTION]
> Make sure to enable RLS policies on the tables!

```sql
create table
public.langgraph_checkpoints (
thread_id text not null,
created_at timestamp with time zone not null default now(),
checkpoint_ns text not null default '',
checkpoint_id text not null,
parent_checkpoint_id text null,
type text null,
checkpoint jsonb null,
metadata jsonb null,
constraint langgraph_checkpoints_pkey primary key (thread_id, checkpoint_ns, checkpoint_id)
) tablespace pg_default;

create table
public.langgraph_writes (
thread_id text not null,
created_at timestamp with time zone not null default now(),
checkpoint_ns text not null default '',
checkpoint_id text not null,
task_id text not null,
idx bigint not null,
channel text not null,
type text null,
value jsonb null,
constraint langgraph_writes_pkey primary key (
thread_id,
checkpoint_ns,
checkpoint_id,
task_id,
idx
)
) tablespace pg_default;

--- Important to disable public access to the tables!
alter table "langgraph_checkpoints" enable row level security;
alter table "langgraph_writes" enable row level security;
```

## Usage

```ts
import { SqliteSaver } from "@langchain/langgraph-checkpoint-supabase";

const writeConfig = {
configurable: {
thread_id: "1",
checkpoint_ns: ""
}
};
const readConfig = {
configurable: {
thread_id: "1"
}
};

const supabaseClient = createClient(SUPABASE_URL, SUPABASE_KEY);
const checkpointer = new SupabaseSaver(supabaseClient, {
checkPointTable: "langgraph_checkpoints",
writeTable: "langgraph_writes",
});

const checkpoint = {
v: 1,
ts: "2024-07-31T20:14:19.804150+00:00",
id: "1ef4f797-8335-6428-8001-8a1503f9b875",
channel_values: {
my_key: "meow",
node: "node"
},
channel_versions: {
__start__: 2,
my_key: 3,
start:node: 3,
node: 3
},
versions_seen: {
__input__: {},
__start__: {
__start__: 1
},
node: {
start:node: 2
}
},
pending_sends: [],
}

// store checkpoint
await checkpointer.put(writeConfig, checkpoint, {}, {})

// load checkpoint
await checkpointer.get(readConfig)

// list checkpoints
for await (const checkpoint of checkpointer.list(readConfig)) {
console.log(checkpoint);
}
```
20 changes: 20 additions & 0 deletions libs/checkpoint-supabase/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: "ts-jest/presets/default-esm",
testEnvironment: "./jest.env.cjs",
modulePathIgnorePatterns: ["dist/"],
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
},
transform: {
"^.+\\.tsx?$": ["@swc/jest"],
},
transformIgnorePatterns: [
"/node_modules/",
"\\.pnp\\.[^\\/]+$",
"./scripts/jest-setup-after-env.js",
],
setupFiles: ["dotenv/config"],
testTimeout: 20_000,
passWithNoTests: true,
};
12 changes: 12 additions & 0 deletions libs/checkpoint-supabase/jest.env.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { TestEnvironment } = require("jest-environment-node");

class AdjustedTestEnvironmentToSupportFloat32Array extends TestEnvironment {
constructor(config, context) {
// Make `instanceof Float32Array` return true in tests
// to avoid https://github.com/xenova/transformers.js/issues/57 and https://github.com/jestjs/jest/issues/2549
super(config, context);
this.global.Float32Array = Float32Array;
}
}

module.exports = AdjustedTestEnvironmentToSupportFloat32Array;
21 changes: 21 additions & 0 deletions libs/checkpoint-supabase/langchain.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";

/**
* @param {string} relativePath
* @returns {string}
*/
function abs(relativePath) {
return resolve(dirname(fileURLToPath(import.meta.url)), relativePath);
}

export const config = {
internals: [/node\:/, /@langchain\/core\//, /async_hooks/],
entrypoints: {
index: "index"
},
tsConfigPath: resolve("./tsconfig.json"),
cjsSource: "./dist-cjs",
cjsDestination: "./dist",
abs,
};
91 changes: 91 additions & 0 deletions libs/checkpoint-supabase/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"name": "@langchain/langgraph-checkpoint-supabase",
"version": "0.1.2",
"description": "LangGraph",
"type": "module",
"engines": {
"node": ">=18"
},
"main": "./index.js",
"types": "./index.d.ts",
"repository": {
"type": "git",
"url": "[email protected]:langchain-ai/langgraphjs.git"
},
"scripts": {
"build": "yarn turbo:command build:internal --filter=@langchain/langgraph-checkpoint-supabase",
"build:internal": "yarn clean && yarn lc_build --create-entrypoints --pre --tree-shaking",
"clean": "rm -rf dist/ dist-cjs/ .turbo/",
"lint:eslint": "NODE_OPTIONS=--max-old-space-size=4096 eslint --cache --ext .ts,.js src/",
"lint:dpdm": "dpdm --exit-code circular:1 --no-warning --no-tree src/*.ts src/**/*.ts",
"lint": "yarn lint:eslint && yarn lint:dpdm",
"lint:fix": "yarn lint:eslint --fix && yarn lint:dpdm",
"prepack": "yarn build",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --testPathIgnorePatterns=\\.int\\.test.ts --testTimeout 30000 --maxWorkers=50%",
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch --testPathIgnorePatterns=\\.int\\.test.ts",
"test:single": "NODE_OPTIONS=--experimental-vm-modules yarn run jest --config jest.config.cjs --testTimeout 100000",
"test:int": "NODE_OPTIONS=--experimental-vm-modules jest --testPathPattern=\\.int\\.test.ts --testTimeout 100000 --maxWorkers=50%",
"format": "prettier --config .prettierrc --write \"src\"",
"format:check": "prettier --config .prettierrc --check \"src\""
},
"author": "LangChain",
"license": "MIT",
"dependencies": {
"@supabase/supabase-js": "^2.45.6"
},
"peerDependencies": {
"@langchain/core": ">=0.2.31 <0.4.0",
"@langchain/langgraph-checkpoint": "~0.0.6"
},
"devDependencies": {
"@jest/globals": "^29.5.0",
"@langchain/langgraph-checkpoint": "workspace:*",
"@langchain/scripts": ">=0.1.3 <0.2.0",
"@swc/core": "^1.3.90",
"@swc/jest": "^0.2.29",
"@tsconfig/recommended": "^1.0.3",
"@types/uuid": "^10",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"dotenv": "^16.3.1",
"dpdm": "^3.12.0",
"eslint": "^8.33.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^28.8.0",
"eslint-plugin-no-instanceof": "^1.0.1",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.5.0",
"jest-environment-node": "^29.6.4",
"prettier": "^2.8.3",
"release-it": "^17.6.0",
"rollup": "^4.23.0",
"ts-jest": "^29.1.0",
"tsx": "^4.7.0",
"typescript": "^4.9.5 || ^5.4.5"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"exports": {
".": {
"types": {
"import": "./index.d.ts",
"require": "./index.d.cts",
"default": "./index.d.ts"
},
"import": "./index.js",
"require": "./index.cjs"
},
"./package.json": "./package.json"
},
"files": [
"dist/",
"index.cjs",
"index.js",
"index.d.ts",
"index.d.cts"
]
}
Loading