langgraph-checkpointer-couchbase 1.0.7__tar.gz → 1.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/PKG-INFO +8 -6
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/async_cb_saver.py +16 -12
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/couchbase_saver.py +17 -14
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/pyproject.toml +8 -6
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/tests/agent_e2e_test.py +40 -22
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.env.example +0 -0
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.github/workflows/release.yaml +0 -0
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.gitignore +0 -0
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/LICENSE +0 -0
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/README.md +0 -0
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/__init__.py +0 -0
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/utils.py +0 -0
- {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-checkpointer-couchbase
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Project-URL: Documentation, https://github.com/couchbase-ecosystem/langgraph-checkpointer-couchbase#readme
|
|
5
5
|
Project-URL: Issues, https://github.com/couchbase-ecosystem/langgraph-checkpointer-couchbase/issues
|
|
6
6
|
Project-URL: Source, https://github.com/couchbase-ecosystem/langgraph-checkpointer-couchbase
|
|
@@ -18,11 +18,13 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
19
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
20
|
Requires-Python: >=3.8
|
|
21
|
-
Requires-Dist: couchbase>=4.
|
|
22
|
-
Requires-Dist: langchain-openai>=
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: couchbase>=4.5.0
|
|
22
|
+
Requires-Dist: langchain-openai>=1.1.3
|
|
23
|
+
Requires-Dist: langchain>=1.1.3
|
|
24
|
+
Requires-Dist: langgraph>=1.0.5
|
|
25
|
+
Requires-Dist: pydantic>=2.12.5
|
|
26
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
27
|
+
Requires-Dist: typing-extensions>=4.15.0
|
|
26
28
|
Description-Content-Type: text/markdown
|
|
27
29
|
|
|
28
30
|
# LangGraph Checkpoint Couchbase
|
|
@@ -22,6 +22,11 @@ from .utils import _encode_binary, _decode_binary
|
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
|
+
# Default timeout for database operations
|
|
26
|
+
DEFAULT_TIMEOUT = timedelta(seconds=5)
|
|
27
|
+
# Default serialization type for metadata (for backward compatibility)
|
|
28
|
+
DEFAULT_METADATA_TYPE = "json"
|
|
29
|
+
|
|
25
30
|
class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
26
31
|
"""A checkpoint saver that stores checkpoints in a Couchbase database."""
|
|
27
32
|
|
|
@@ -94,10 +99,6 @@ class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
|
94
99
|
auth = PasswordAuthenticator(cb_username, cb_password)
|
|
95
100
|
options = ClusterOptions(auth)
|
|
96
101
|
cluster = await ACluster.connect(cb_conn_str, options)
|
|
97
|
-
|
|
98
|
-
cls.cluster = cluster
|
|
99
|
-
cls.bucket_name = bucket_name
|
|
100
|
-
cls.scope_name = scope_name
|
|
101
102
|
|
|
102
103
|
bucket = cluster.bucket(bucket_name)
|
|
103
104
|
await bucket.on_connect()
|
|
@@ -211,7 +212,7 @@ class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
|
211
212
|
return CheckpointTuple(
|
|
212
213
|
{"configurable": config_values},
|
|
213
214
|
checkpoint,
|
|
214
|
-
self.serde.
|
|
215
|
+
self.serde.loads_typed((doc.get("metadata_type", DEFAULT_METADATA_TYPE), _decode_binary(doc["metadata"]))) if doc.get("metadata") else None,
|
|
215
216
|
(
|
|
216
217
|
{
|
|
217
218
|
"configurable": {
|
|
@@ -226,6 +227,8 @@ class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
|
226
227
|
pending_writes,
|
|
227
228
|
)
|
|
228
229
|
|
|
230
|
+
return None
|
|
231
|
+
|
|
229
232
|
async def alist(
|
|
230
233
|
self,
|
|
231
234
|
config: Optional[RunnableConfig],
|
|
@@ -285,7 +288,7 @@ class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
|
285
288
|
}
|
|
286
289
|
},
|
|
287
290
|
checkpoint,
|
|
288
|
-
self.serde.
|
|
291
|
+
self.serde.loads_typed((doc.get("metadata_type", DEFAULT_METADATA_TYPE), _decode_binary(doc["metadata"]))) if doc.get("metadata") else None,
|
|
289
292
|
(
|
|
290
293
|
{
|
|
291
294
|
"configurable": {
|
|
@@ -327,15 +330,16 @@ class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
|
327
330
|
if serialized_checkpoint:
|
|
328
331
|
serialized_checkpoint = _encode_binary(serialized_checkpoint)
|
|
329
332
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
+
# Serialize and encode metadata
|
|
334
|
+
metadata_type, metadata_bytes = self.serde.dumps_typed(metadata)
|
|
335
|
+
serialized_metadata = _encode_binary(metadata_bytes) if metadata_bytes else None
|
|
333
336
|
|
|
334
337
|
doc = {
|
|
335
338
|
"parent_checkpoint_id": config["configurable"].get("checkpoint_id"),
|
|
336
339
|
"type": type_,
|
|
337
340
|
"checkpoint": serialized_checkpoint,
|
|
338
|
-
"metadata":
|
|
341
|
+
"metadata": serialized_metadata,
|
|
342
|
+
"metadata_type": metadata_type,
|
|
339
343
|
"thread_id" : thread_id,
|
|
340
344
|
"checkpoint_ns": checkpoint_ns,
|
|
341
345
|
"checkpoint_id": checkpoint_id,
|
|
@@ -346,7 +350,7 @@ class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
|
346
350
|
# ensure bucket connected (idempotent)
|
|
347
351
|
await self.bucket.on_connect()
|
|
348
352
|
collection = self.bucket.scope(self.scope_name).collection(self.checkpoints_collection_name)
|
|
349
|
-
await collection.upsert(upsert_key, (doc), UpsertOptions(timeout=
|
|
353
|
+
await collection.upsert(upsert_key, (doc), UpsertOptions(timeout=DEFAULT_TIMEOUT))
|
|
350
354
|
|
|
351
355
|
return {
|
|
352
356
|
"configurable": {
|
|
@@ -394,4 +398,4 @@ class AsyncCouchbaseSaver(BaseCheckpointSaver):
|
|
|
394
398
|
"type": type_,
|
|
395
399
|
"value": serialized_value,
|
|
396
400
|
}
|
|
397
|
-
await collection.upsert(upsert_key, (doc), UpsertOptions(timeout=
|
|
401
|
+
await collection.upsert(upsert_key, (doc), UpsertOptions(timeout=DEFAULT_TIMEOUT))
|
|
@@ -22,6 +22,11 @@ from langgraph.checkpoint.base import (
|
|
|
22
22
|
from .utils import _encode_binary, _decode_binary
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Default timeout for database operations
|
|
27
|
+
DEFAULT_TIMEOUT = timedelta(seconds=5)
|
|
28
|
+
# Default serialization type for metadata (for backward compatibility)
|
|
29
|
+
DEFAULT_METADATA_TYPE = "json"
|
|
25
30
|
class CouchbaseSaver(BaseCheckpointSaver):
|
|
26
31
|
"""A checkpoint saver that stores checkpoints in a Couchbase database.
|
|
27
32
|
|
|
@@ -77,11 +82,7 @@ class CouchbaseSaver(BaseCheckpointSaver):
|
|
|
77
82
|
auth = PasswordAuthenticator(cb_username, cb_password)
|
|
78
83
|
options = ClusterOptions(auth)
|
|
79
84
|
cluster = Cluster(cb_conn_str, options)
|
|
80
|
-
cluster.wait_until_ready(
|
|
81
|
-
|
|
82
|
-
cls.cluster = cluster
|
|
83
|
-
cls.bucket_name = bucket_name
|
|
84
|
-
cls.scope_name = scope_name
|
|
85
|
+
cluster.wait_until_ready(DEFAULT_TIMEOUT)
|
|
85
86
|
|
|
86
87
|
saver = CouchbaseSaver(cluster, bucket_name, scope_name, checkpoints_collection_name, checkpoint_writes_collection_name)
|
|
87
88
|
|
|
@@ -197,8 +198,8 @@ class CouchbaseSaver(BaseCheckpointSaver):
|
|
|
197
198
|
)
|
|
198
199
|
|
|
199
200
|
# Decode and deserialize metadata
|
|
200
|
-
|
|
201
|
-
metadata = self.serde.
|
|
201
|
+
metadata_data = _decode_binary(doc["metadata"])
|
|
202
|
+
metadata = self.serde.loads_typed((doc.get("metadata_type", DEFAULT_METADATA_TYPE), metadata_data))
|
|
202
203
|
|
|
203
204
|
return CheckpointTuple(
|
|
204
205
|
{"configurable": config_values},
|
|
@@ -218,6 +219,8 @@ class CouchbaseSaver(BaseCheckpointSaver):
|
|
|
218
219
|
pending_writes,
|
|
219
220
|
)
|
|
220
221
|
|
|
222
|
+
return None
|
|
223
|
+
|
|
221
224
|
def list(
|
|
222
225
|
self,
|
|
223
226
|
config: Optional[RunnableConfig],
|
|
@@ -277,7 +280,7 @@ class CouchbaseSaver(BaseCheckpointSaver):
|
|
|
277
280
|
}
|
|
278
281
|
},
|
|
279
282
|
checkpoint,
|
|
280
|
-
self.serde.
|
|
283
|
+
self.serde.loads_typed((doc.get("metadata_type", DEFAULT_METADATA_TYPE), _decode_binary(doc["metadata"]))),
|
|
281
284
|
(
|
|
282
285
|
{
|
|
283
286
|
"configurable": {
|
|
@@ -322,15 +325,15 @@ class CouchbaseSaver(BaseCheckpointSaver):
|
|
|
322
325
|
serialized_checkpoint = _encode_binary(serialized_checkpoint)
|
|
323
326
|
|
|
324
327
|
# Serialize and encode metadata
|
|
325
|
-
metadata_bytes = self.serde.
|
|
326
|
-
if metadata_bytes
|
|
327
|
-
metadata = _encode_binary(metadata_bytes)
|
|
328
|
+
metadata_type, metadata_bytes = self.serde.dumps_typed(metadata)
|
|
329
|
+
serialized_metadata = _encode_binary(metadata_bytes) if metadata_bytes else None
|
|
328
330
|
|
|
329
331
|
doc = {
|
|
330
332
|
"parent_checkpoint_id": config["configurable"].get("checkpoint_id"),
|
|
331
333
|
"type": type_,
|
|
332
334
|
"checkpoint": serialized_checkpoint,
|
|
333
|
-
"metadata":
|
|
335
|
+
"metadata": serialized_metadata,
|
|
336
|
+
"metadata_type": metadata_type,
|
|
334
337
|
"thread_id" : thread_id,
|
|
335
338
|
"checkpoint_ns": checkpoint_ns,
|
|
336
339
|
"checkpoint_id": checkpoint_id,
|
|
@@ -338,7 +341,7 @@ class CouchbaseSaver(BaseCheckpointSaver):
|
|
|
338
341
|
upsert_key = f"{thread_id}::{checkpoint_ns}::{checkpoint_id}"
|
|
339
342
|
|
|
340
343
|
collection = self.checkpoints_collection
|
|
341
|
-
collection.upsert(upsert_key, (doc), UpsertOptions(timeout=
|
|
344
|
+
collection.upsert(upsert_key, (doc), UpsertOptions(timeout=DEFAULT_TIMEOUT))
|
|
342
345
|
|
|
343
346
|
return {
|
|
344
347
|
"configurable": {
|
|
@@ -387,4 +390,4 @@ class CouchbaseSaver(BaseCheckpointSaver):
|
|
|
387
390
|
"type": type_,
|
|
388
391
|
"value": serialized_value,
|
|
389
392
|
}
|
|
390
|
-
collection.upsert(upsert_key,
|
|
393
|
+
collection.upsert(upsert_key, doc, UpsertOptions(timeout=DEFAULT_TIMEOUT))
|
{langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/pyproject.toml
RENAMED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "langgraph-checkpointer-couchbase"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.8"
|
|
8
8
|
description = ''
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -25,11 +25,13 @@ classifiers = [
|
|
|
25
25
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
26
26
|
]
|
|
27
27
|
dependencies = [
|
|
28
|
-
"couchbase>=4.
|
|
29
|
-
"langgraph>=0.
|
|
30
|
-
"langchain-openai>=
|
|
31
|
-
"pydantic>=2.
|
|
32
|
-
"typing_extensions>=4.
|
|
28
|
+
"couchbase>=4.5.0",
|
|
29
|
+
"langgraph>=1.0.5",
|
|
30
|
+
"langchain-openai>=1.1.3",
|
|
31
|
+
"pydantic>=2.12.5",
|
|
32
|
+
"typing_extensions>=4.15.0",
|
|
33
|
+
"langchain>=1.1.3",
|
|
34
|
+
"python-dotenv>=1.0.0",
|
|
33
35
|
]
|
|
34
36
|
|
|
35
37
|
[project.urls]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from typing import Literal
|
|
3
|
-
from langchain_core.runnables import ConfigurableField
|
|
4
3
|
from langchain_core.tools import tool
|
|
5
4
|
from langchain_openai import ChatOpenAI
|
|
6
|
-
from
|
|
5
|
+
from langchain.agents import create_agent
|
|
7
6
|
from langgraph_checkpointer_couchbase import CouchbaseSaver, AsyncCouchbaseSaver
|
|
8
7
|
from dotenv import load_dotenv
|
|
9
8
|
import os
|
|
9
|
+
|
|
10
10
|
load_dotenv()
|
|
11
11
|
|
|
12
12
|
@tool
|
|
@@ -21,17 +21,22 @@ def get_weather(city: Literal["nyc", "sf"]):
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
tools = [get_weather]
|
|
24
|
-
model = ChatOpenAI(
|
|
24
|
+
model = ChatOpenAI(model="gpt-5-mini", temperature=0)
|
|
25
|
+
|
|
25
26
|
|
|
26
27
|
def syncTest():
|
|
27
28
|
with CouchbaseSaver.from_conn_info(
|
|
28
|
-
cb_conn_str=os.getenv("CB_CLUSTER")
|
|
29
|
-
cb_username=os.getenv("CB_USERNAME")
|
|
30
|
-
cb_password=os.getenv("CB_PASSWORD")
|
|
31
|
-
bucket_name=os.getenv("CB_BUCKET")
|
|
32
|
-
scope_name=os.getenv("CB_SCOPE")
|
|
29
|
+
cb_conn_str=os.getenv("CB_CLUSTER"),
|
|
30
|
+
cb_username=os.getenv("CB_USERNAME"),
|
|
31
|
+
cb_password=os.getenv("CB_PASSWORD"),
|
|
32
|
+
bucket_name=os.getenv("CB_BUCKET"),
|
|
33
|
+
scope_name=os.getenv("CB_SCOPE"),
|
|
33
34
|
) as checkpointer:
|
|
34
|
-
graph =
|
|
35
|
+
graph = create_agent(
|
|
36
|
+
model,
|
|
37
|
+
tools=tools,
|
|
38
|
+
checkpointer=checkpointer,
|
|
39
|
+
)
|
|
35
40
|
config = {"configurable": {"thread_id": "1"}}
|
|
36
41
|
res = graph.invoke({"messages": [("human", "what's the weather in sf")]}, config)
|
|
37
42
|
|
|
@@ -39,19 +44,26 @@ def syncTest():
|
|
|
39
44
|
latest_checkpoint_tuple = checkpointer.get_tuple(config)
|
|
40
45
|
checkpoint_tuples = list(checkpointer.list(config))
|
|
41
46
|
|
|
42
|
-
print(
|
|
43
|
-
print(
|
|
44
|
-
print(
|
|
47
|
+
print("=== Sync Test Results ===")
|
|
48
|
+
print(f"Response: {res}")
|
|
49
|
+
print(f"Latest checkpoint: {latest_checkpoint}")
|
|
50
|
+
print(f"Latest checkpoint tuple: {latest_checkpoint_tuple}")
|
|
51
|
+
print(f"All checkpoint tuples: {checkpoint_tuples}")
|
|
52
|
+
|
|
45
53
|
|
|
46
54
|
async def asyncTest():
|
|
47
55
|
async with AsyncCouchbaseSaver.from_conn_info(
|
|
48
|
-
cb_conn_str=os.getenv("CB_CLUSTER")
|
|
49
|
-
cb_username=os.getenv("CB_USERNAME")
|
|
50
|
-
cb_password=os.getenv("CB_PASSWORD")
|
|
51
|
-
bucket_name=os.getenv("CB_BUCKET")
|
|
52
|
-
scope_name=os.getenv("CB_SCOPE")
|
|
56
|
+
cb_conn_str=os.getenv("CB_CLUSTER"),
|
|
57
|
+
cb_username=os.getenv("CB_USERNAME"),
|
|
58
|
+
cb_password=os.getenv("CB_PASSWORD"),
|
|
59
|
+
bucket_name=os.getenv("CB_BUCKET"),
|
|
60
|
+
scope_name=os.getenv("CB_SCOPE"),
|
|
53
61
|
) as checkpointer:
|
|
54
|
-
graph =
|
|
62
|
+
graph = create_agent(
|
|
63
|
+
model,
|
|
64
|
+
tools=tools,
|
|
65
|
+
checkpointer=checkpointer,
|
|
66
|
+
)
|
|
55
67
|
config = {"configurable": {"thread_id": "2"}}
|
|
56
68
|
res = await graph.ainvoke(
|
|
57
69
|
{"messages": [("human", "what's the weather in nyc")]}, config
|
|
@@ -61,10 +73,16 @@ async def asyncTest():
|
|
|
61
73
|
latest_checkpoint_tuple = await checkpointer.aget_tuple(config)
|
|
62
74
|
checkpoint_tuples = [c async for c in checkpointer.alist(config)]
|
|
63
75
|
|
|
64
|
-
print(
|
|
65
|
-
print(
|
|
66
|
-
print(
|
|
76
|
+
print("=== Async Test Results ===")
|
|
77
|
+
print(f"Response: {res}")
|
|
78
|
+
print(f"Latest checkpoint: {latest_checkpoint}")
|
|
79
|
+
print(f"Latest checkpoint tuple: {latest_checkpoint_tuple}")
|
|
80
|
+
print(f"All checkpoint tuples: {checkpoint_tuples}")
|
|
81
|
+
|
|
67
82
|
|
|
68
83
|
if __name__ == "__main__":
|
|
84
|
+
print("Running sync test...")
|
|
69
85
|
syncTest()
|
|
70
|
-
|
|
86
|
+
print("\nRunning async test...")
|
|
87
|
+
asyncio.run(asyncTest())
|
|
88
|
+
print("\nAll tests completed!")
|
{langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.env.example
RENAMED
|
File without changes
|
|
File without changes
|
{langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/tests/__init__.py
RENAMED
|
File without changes
|