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.
Files changed (13) hide show
  1. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/PKG-INFO +8 -6
  2. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/async_cb_saver.py +16 -12
  3. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/couchbase_saver.py +17 -14
  4. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/pyproject.toml +8 -6
  5. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/tests/agent_e2e_test.py +40 -22
  6. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.env.example +0 -0
  7. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.github/workflows/release.yaml +0 -0
  8. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/.gitignore +0 -0
  9. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/LICENSE +0 -0
  10. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/README.md +0 -0
  11. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/__init__.py +0 -0
  12. {langgraph_checkpointer_couchbase-1.0.7 → langgraph_checkpointer_couchbase-1.0.8}/langgraph_checkpointer_couchbase/utils.py +0 -0
  13. {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.7
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.3.5
22
- Requires-Dist: langchain-openai>=0.3.11
23
- Requires-Dist: langgraph>=0.3.22
24
- Requires-Dist: pydantic>=2.11.1
25
- Requires-Dist: typing-extensions>=4.13.0
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.loads(_decode_binary(doc["metadata"])),
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.loads(_decode_binary(doc["metadata"])),
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
- metadata = self.serde.dumps(metadata)
331
- if metadata:
332
- metadata = _encode_binary(metadata)
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": 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=timedelta(seconds=5)))
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=timedelta(seconds=5)))
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(timedelta(seconds=5))
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
- metadata = _decode_binary(doc["metadata"])
201
- metadata = self.serde.loads(metadata)
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.loads(_decode_binary(doc["metadata"])),
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.dumps(metadata)
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": 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=timedelta(seconds=5)))
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, (doc), UpsertOptions(timeout=timedelta(seconds=5)))
393
+ collection.upsert(upsert_key, doc, UpsertOptions(timeout=DEFAULT_TIMEOUT))
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "langgraph-checkpointer-couchbase"
7
- version = "1.0.7"
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.3.5",
29
- "langgraph>=0.3.22",
30
- "langchain-openai>=0.3.11",
31
- "pydantic>=2.11.1",
32
- "typing_extensions>=4.13.0"
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 langgraph.prebuilt import create_react_agent
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(model_name="gpt-4o-mini", temperature=0)
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") or "couchbase://localhost",
29
- cb_username=os.getenv("CB_USERNAME") or "Administrator",
30
- cb_password=os.getenv("CB_PASSWORD") or "password",
31
- bucket_name=os.getenv("CB_BUCKET") or "test",
32
- scope_name=os.getenv("CB_SCOPE") or "langgraph",
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 = create_react_agent(model, tools=tools, checkpointer=checkpointer)
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(latest_checkpoint)
43
- print(latest_checkpoint_tuple)
44
- print(checkpoint_tuples)
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") or "couchbase://localhost",
49
- cb_username=os.getenv("CB_USERNAME") or "Administrator",
50
- cb_password=os.getenv("CB_PASSWORD") or "password",
51
- bucket_name=os.getenv("CB_BUCKET") or "test",
52
- scope_name=os.getenv("CB_SCOPE") or "langgraph",
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 = create_react_agent(model, tools=tools, checkpointer=checkpointer)
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(latest_checkpoint)
65
- print(latest_checkpoint_tuple)
66
- print(checkpoint_tuples)
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
- asyncio.run(asyncTest())
86
+ print("\nRunning async test...")
87
+ asyncio.run(asyncTest())
88
+ print("\nAll tests completed!")