langchain-postgres 0.0.4__py3-none-any.whl → 0.0.6__py3-none-any.whl

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.
@@ -1,11 +1,6 @@
1
1
  from importlib import metadata
2
2
 
3
3
  from langchain_postgres.chat_message_histories import PostgresChatMessageHistory
4
- from langchain_postgres.checkpoint import (
5
- CheckpointSerializer,
6
- PickleCheckpointSerializer,
7
- PostgresSaver,
8
- )
9
4
  from langchain_postgres.vectorstores import PGVector
10
5
 
11
6
  try:
@@ -16,9 +11,6 @@ except metadata.PackageNotFoundError:
16
11
 
17
12
  __all__ = [
18
13
  "__version__",
19
- "CheckpointSerializer",
20
14
  "PostgresChatMessageHistory",
21
- "PostgresSaver",
22
- "PickleCheckpointSerializer",
23
15
  "PGVector",
24
16
  ]
@@ -66,6 +66,7 @@ SPECIAL_CASED_OPERATORS = {
66
66
  "$in",
67
67
  "$nin",
68
68
  "$between",
69
+ "$exists",
69
70
  }
70
71
 
71
72
  TEXT_OPERATORS = {
@@ -702,13 +703,24 @@ class PGVector(VectorStore):
702
703
  if operator in {"$in"}:
703
704
  return queried_field.in_([str(val) for val in filter_value])
704
705
  elif operator in {"$nin"}:
705
- return queried_field.nin_([str(val) for val in filter_value])
706
+ return ~queried_field.in_([str(val) for val in filter_value])
706
707
  elif operator in {"$like"}:
707
708
  return queried_field.like(filter_value)
708
709
  elif operator in {"$ilike"}:
709
710
  return queried_field.ilike(filter_value)
710
711
  else:
711
712
  raise NotImplementedError()
713
+ elif operator == "$exists":
714
+ if not isinstance(filter_value, bool):
715
+ raise ValueError(
716
+ "Expected a boolean value for $exists "
717
+ f"operator, but got: {filter_value}"
718
+ )
719
+ condition = func.jsonb_exists(
720
+ self.EmbeddingStore.cmetadata,
721
+ field,
722
+ )
723
+ return condition if filter_value else ~condition
712
724
  else:
713
725
  raise NotImplementedError()
714
726
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langchain-postgres
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: An integration package connecting Postgres and LangChain
5
5
  Home-page: https://github.com/langchain-ai/langchain-postgres
6
6
  License: MIT
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Requires-Dist: langchain-core (>=0.1.50,<0.3)
15
- Requires-Dist: langgraph (>=0.0.32,<0.0.33)
16
15
  Requires-Dist: numpy (>=1,<2)
17
16
  Requires-Dist: pgvector (>=0.2.5,<0.3.0)
18
17
  Requires-Dist: psycopg (>=3,<4)
@@ -48,97 +47,6 @@ pip install -U langchain-postgres
48
47
 
49
48
  ## Usage
50
49
 
51
- ### PostgresSaver (LangGraph Checkpointer)
52
-
53
- The LangGraph checkpointer can be used to add memory to your LangGraph application.
54
-
55
- `PostgresSaver` is an implementation of the checkpointer saver using
56
- Postgres as the backend.
57
-
58
- Currently, only the psycopg3 driver is supported.
59
-
60
- Sync usage:
61
-
62
- ```python
63
- from psycopg_pool import ConnectionPool
64
- from langchain_postgres import (
65
- PostgresSaver, PickleCheckpointSerializer
66
- )
67
-
68
- pool = ConnectionPool(
69
- # Example configuration
70
- conninfo="postgresql://langchain:langchain@localhost:6024/langchain",
71
- max_size=20,
72
- )
73
-
74
- PostgresSaver.create_tables(pool)
75
-
76
- checkpointer = PostgresSaver(
77
- serializer=PickleCheckpointSerializer(),
78
- sync_connection=pool,
79
- )
80
-
81
- # Set up the langgraph workflow with the checkpointer
82
- workflow = ... # Fill in with your workflow
83
- app = workflow.compile(checkpointer=checkpointer)
84
-
85
- # Use with the sync methods of `app` (e.g., `app.stream())
86
-
87
- pool.close() # Remember to close the connection pool.
88
- ```
89
-
90
- Async usage:
91
-
92
- ```python
93
- from psycopg_pool import AsyncConnectionPool
94
- from langchain_postgres import (
95
- PostgresSaver, PickleCheckpointSerializer
96
- )
97
-
98
- pool = AsyncConnectionPool(
99
- # Example configuration
100
- conninfo="postgresql://langchain:langchain@localhost:6024/langchain",
101
- max_size=20,
102
- )
103
-
104
- # Create the tables in postgres (only needs to be done once)
105
- await PostgresSaver.acreate_tables(pool)
106
-
107
- checkpointer = PostgresSaver(
108
- serializer=PickleCheckpointSerializer(),
109
- async_connection=pool,
110
- )
111
-
112
- # Set up the langgraph workflow with the checkpointer
113
- workflow = ... # Fill in with your workflow
114
- app = workflow.compile(checkpointer=checkpointer)
115
-
116
- # Use with the async methods of `app` (e.g., `app.astream()`)
117
-
118
- await pool.close() # Remember to close the connection pool.
119
- ```
120
-
121
- #### Testing
122
-
123
- If testing with the postgres checkpointer it may be useful to both create and
124
- drop the tables before and after the tests.
125
-
126
- ```python
127
- from psycopg_pool import ConnectionPool
128
- from langchain_postgres import (
129
- PostgresSaver
130
- )
131
- with ConnectionPool(
132
- # Example configuration
133
- conninfo="postgresql://langchain:langchain@localhost:6024/langchain",
134
- max_size=20,
135
- ) as conn:
136
- PostgresSaver.create_tables(conn)
137
- PostgresSaver.drop_tables(conn)
138
- # Run your unit tests with langgraph
139
- ```
140
-
141
-
142
50
  ### ChatMessageHistory
143
51
 
144
52
  The chat message history abstraction helps to persist chat message history
@@ -0,0 +1,9 @@
1
+ langchain_postgres/__init__.py,sha256=9rXsSowLZpH_dqyWJUhpTVwviQ02wLTZj_kPi6OOfcc,415
2
+ langchain_postgres/_utils.py,sha256=Johm50HEgA4qScLDaSDIfJfF3DygjA5KTBtNeVcD63I,2914
3
+ langchain_postgres/chat_message_histories.py,sha256=gh6hjBlrJ5GSo5kePQdh3VhiUYoWWdP37GXtZ1e25a4,14033
4
+ langchain_postgres/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ langchain_postgres/vectorstores.py,sha256=1LVIONZnVvXRPvFOjKKfjleC8ATMbdbprUJUU3NYFwc,50367
6
+ langchain_postgres-0.0.6.dist-info/LICENSE,sha256=2btS8uNUDWD_UNjw9ba6ZJt_00aUjEw9CGyK-xIHY8c,1072
7
+ langchain_postgres-0.0.6.dist-info/METADATA,sha256=Urbd8Cmyu8N1OEP-m6WXm-mAuKruPtSrStCe_kcxj-4,3697
8
+ langchain_postgres-0.0.6.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
9
+ langchain_postgres-0.0.6.dist-info/RECORD,,
@@ -1,587 +0,0 @@
1
- """Implementation of a langgraph checkpoint saver using Postgres."""
2
- import abc
3
- import pickle
4
- from contextlib import asynccontextmanager, contextmanager
5
- from typing import AsyncGenerator, AsyncIterator, Generator, Optional, Union, cast
6
-
7
- import psycopg
8
- from langchain_core.runnables import ConfigurableFieldSpec, RunnableConfig
9
- from langgraph.checkpoint import BaseCheckpointSaver
10
- from langgraph.checkpoint.base import Checkpoint, CheckpointThreadTs, CheckpointTuple
11
- from psycopg_pool import AsyncConnectionPool, ConnectionPool
12
-
13
-
14
- class CheckpointSerializer(abc.ABC):
15
- """A serializer for serializing and deserializing objects to and from bytes."""
16
-
17
- @abc.abstractmethod
18
- def dumps(self, obj: Checkpoint) -> bytes:
19
- """Serialize an object to bytes."""
20
-
21
- @abc.abstractmethod
22
- def loads(self, data: bytes) -> Checkpoint:
23
- """Deserialize an object from bytes."""
24
-
25
-
26
- class PickleCheckpointSerializer(CheckpointSerializer):
27
- """Use the pickle module to serialize and deserialize objects.
28
-
29
- This serializer uses the pickle module to serialize and deserialize objects.
30
-
31
- While pickling can serialize a wide range of Python objects, it may fail
32
- de-serializable objects upon updates of the Python version or the python
33
- environment (e.g., the object's class definition changes in LangGraph).
34
-
35
- *Security Warning*: The pickle module can deserialize malicious payloads,
36
- only use this serializer with trusted data; e.g., data that you
37
- have serialized yourself and can guarantee the integrity of.
38
- """
39
-
40
- def dumps(self, obj: Checkpoint) -> bytes:
41
- """Serialize an object to bytes."""
42
- return pickle.dumps(obj)
43
-
44
- def loads(self, data: bytes) -> Checkpoint:
45
- """Deserialize an object from bytes."""
46
- return cast(Checkpoint, pickle.loads(data))
47
-
48
-
49
- @contextmanager
50
- def _get_sync_connection(
51
- connection: Union[psycopg.Connection, ConnectionPool, None],
52
- ) -> Generator[psycopg.Connection, None, None]:
53
- """Get the connection to the Postgres database."""
54
- if isinstance(connection, psycopg.Connection):
55
- yield connection
56
- elif isinstance(connection, ConnectionPool):
57
- with connection.connection() as conn:
58
- yield conn
59
- else:
60
- raise ValueError(
61
- "Invalid sync connection object. Please initialize the check pointer "
62
- f"with an appropriate sync connection object. "
63
- f"Got {type(connection)}."
64
- )
65
-
66
-
67
- @asynccontextmanager
68
- async def _get_async_connection(
69
- connection: Union[psycopg.AsyncConnection, AsyncConnectionPool, None],
70
- ) -> AsyncGenerator[psycopg.AsyncConnection, None]:
71
- """Get the connection to the Postgres database."""
72
- if isinstance(connection, psycopg.AsyncConnection):
73
- yield connection
74
- elif isinstance(connection, AsyncConnectionPool):
75
- async with connection.connection() as conn:
76
- yield conn
77
- else:
78
- raise ValueError(
79
- "Invalid async connection object. Please initialize the check pointer "
80
- f"with an appropriate async connection object. "
81
- f"Got {type(connection)}."
82
- )
83
-
84
-
85
- class PostgresSaver(BaseCheckpointSaver):
86
- """LangGraph checkpoint saver for Postgres.
87
-
88
- This implementation of a checkpoint saver uses a Postgres database to save
89
- and retrieve checkpoints. It uses the psycopg3 package to interact with the
90
- Postgres database.
91
-
92
- The checkpoint accepts either a sync_connection in the form of a psycopg.Connection
93
- or a psycopg.ConnectionPool object, or an async_connection in the form of a
94
- psycopg.AsyncConnection or psycopg.AsyncConnectionPool object.
95
-
96
- Usage:
97
-
98
- 1. First time use: create schema in the database using the `create_tables` method or
99
- the async version `acreate_tables` method.
100
- 2. Create a PostgresCheckpoint object with a serializer and an appropriate
101
- connection object.
102
- It's recommended to use a connection pool object for the connection.
103
- If using a connection object, you are responsible for closing the connection
104
- when done.
105
-
106
- Examples:
107
-
108
-
109
- Sync usage with a connection pool:
110
-
111
- .. code-block:: python
112
-
113
- from psycopg_pool import ConnectionPool
114
- from langchain_postgres import (
115
- PostgresCheckpoint, PickleCheckpointSerializer
116
- )
117
-
118
- pool = ConnectionPool(
119
- # Example configuration
120
- conninfo="postgresql://user:password@localhost:5432/dbname",
121
- max_size=20,
122
- )
123
-
124
- # Uses the pickle module for serialization
125
- # Make sure that you're only de-serializing trusted data
126
- # (e.g., payloads that you have serialized yourself).
127
- # Or implement a custom serializer.
128
- checkpoint = PostgresCheckpoint(
129
- serializer=PickleCheckpointSerializer(),
130
- sync_connection=pool,
131
- )
132
-
133
- # Use the checkpoint object to put, get, list checkpoints, etc.
134
-
135
-
136
- Async usage with a connection pool:
137
-
138
- .. code-block:: python
139
-
140
- from psycopg_pool import AsyncConnectionPool
141
- from langchain_postgres import (
142
- PostgresCheckpoint, PickleCheckpointSerializer
143
- )
144
-
145
- pool = AsyncConnectionPool(
146
- # Example configuration
147
- conninfo="postgresql://user:password@localhost:5432/dbname",
148
- max_size=20,
149
- )
150
-
151
- # Uses the pickle module for serialization
152
- # Make sure that you're only de-serializing trusted data
153
- # (e.g., payloads that you have serialized yourself).
154
- # Or implement a custom serializer.
155
- checkpoint = PostgresCheckpoint(
156
- serializer=PickleCheckpointSerializer(),
157
- async_connection=pool,
158
- )
159
-
160
- # Use the checkpoint object to put, get, list checkpoints, etc.
161
-
162
-
163
- Async usage with a connection object:
164
-
165
- .. code-block:: python
166
-
167
- from psycopg import AsyncConnection
168
- from langchain_postgres import (
169
- PostgresCheckpoint, PickleCheckpointSerializer
170
- )
171
-
172
- conninfo="postgresql://user:password@localhost:5432/dbname"
173
- # Take care of closing the connection when done
174
- async with AsyncConnection(conninfo=conninfo) as conn:
175
- # Uses the pickle module for serialization
176
- # Make sure that you're only de-serializing trusted data
177
- # (e.g., payloads that you have serialized yourself).
178
- # Or implement a custom serializer.
179
- checkpoint = PostgresCheckpoint(
180
- serializer=PickleCheckpointSerializer(),
181
- async_connection=conn,
182
- )
183
-
184
- # Use the checkpoint object to put, get, list checkpoints, etc.
185
- ...
186
- """
187
-
188
- serializer: CheckpointSerializer
189
- """The serializer for serializing and deserializing objects to and from bytes."""
190
-
191
- sync_connection: Optional[Union[psycopg.Connection, ConnectionPool]] = None
192
- """The synchronous connection or pool to the Postgres database.
193
-
194
- If providing a connection object, please ensure that the connection is open
195
- and remember to close the connection when done.
196
- """
197
- async_connection: Optional[
198
- Union[psycopg.AsyncConnection, AsyncConnectionPool]
199
- ] = None
200
- """The asynchronous connection or pool to the Postgres database.
201
-
202
- If providing a connection object, please ensure that the connection is open
203
- and remember to close the connection when done.
204
- """
205
-
206
- class Config:
207
- arbitrary_types_allowed = True
208
- extra = "forbid"
209
-
210
- @property
211
- def config_specs(self) -> list[ConfigurableFieldSpec]:
212
- """Return the configuration specs for this runnable."""
213
- return [
214
- ConfigurableFieldSpec(
215
- id="thread_id",
216
- annotation=Optional[str],
217
- name="Thread ID",
218
- description=None,
219
- default=None,
220
- is_shared=True,
221
- ),
222
- CheckpointThreadTs,
223
- ]
224
-
225
- @contextmanager
226
- def _get_sync_connection(self) -> Generator[psycopg.Connection, None, None]:
227
- """Get the connection to the Postgres database."""
228
- with _get_sync_connection(self.sync_connection) as connection:
229
- yield connection
230
-
231
- @asynccontextmanager
232
- async def _get_async_connection(
233
- self,
234
- ) -> AsyncGenerator[psycopg.AsyncConnection, None]:
235
- """Get the connection to the Postgres database."""
236
- async with _get_async_connection(self.async_connection) as connection:
237
- yield connection
238
-
239
- @staticmethod
240
- def create_tables(connection: Union[psycopg.Connection, ConnectionPool], /) -> None:
241
- """Create the schema for the checkpoint saver."""
242
- with _get_sync_connection(connection) as conn:
243
- with conn.cursor() as cur:
244
- cur.execute(
245
- """
246
- CREATE TABLE IF NOT EXISTS checkpoints (
247
- thread_id TEXT NOT NULL,
248
- checkpoint BYTEA NOT NULL,
249
- thread_ts TIMESTAMPTZ NOT NULL,
250
- parent_ts TIMESTAMPTZ,
251
- PRIMARY KEY (thread_id, thread_ts)
252
- );
253
- """
254
- )
255
-
256
- @staticmethod
257
- async def acreate_tables(
258
- connection: Union[psycopg.AsyncConnection, AsyncConnectionPool], /
259
- ) -> None:
260
- """Create the schema for the checkpoint saver."""
261
- async with _get_async_connection(connection) as conn:
262
- async with conn.cursor() as cur:
263
- await cur.execute(
264
- """
265
- CREATE TABLE IF NOT EXISTS checkpoints (
266
- thread_id TEXT NOT NULL,
267
- checkpoint BYTEA NOT NULL,
268
- thread_ts TIMESTAMPTZ NOT NULL,
269
- parent_ts TIMESTAMPTZ,
270
- PRIMARY KEY (thread_id, thread_ts)
271
- );
272
- """
273
- )
274
-
275
- @staticmethod
276
- def drop_tables(connection: psycopg.Connection, /) -> None:
277
- """Drop the table for the checkpoint saver."""
278
- with connection.cursor() as cur:
279
- cur.execute("DROP TABLE IF EXISTS checkpoints;")
280
-
281
- @staticmethod
282
- async def adrop_tables(connection: psycopg.AsyncConnection, /) -> None:
283
- """Drop the table for the checkpoint saver."""
284
- async with connection.cursor() as cur:
285
- await cur.execute("DROP TABLE IF EXISTS checkpoints;")
286
-
287
- def put(self, config: RunnableConfig, checkpoint: Checkpoint) -> RunnableConfig:
288
- """Put the checkpoint for the given configuration.
289
-
290
- Args:
291
- config: The configuration for the checkpoint.
292
- A dict with a `configurable` key which is a dict with
293
- a `thread_id` key and an optional `thread_ts` key.
294
- For example, { 'configurable': { 'thread_id': 'test_thread' } }
295
- checkpoint: The checkpoint to persist.
296
-
297
- Returns:
298
- The RunnableConfig that describes the checkpoint that was just created.
299
- It'll contain the `thread_id` and `thread_ts` of the checkpoint.
300
- """
301
- thread_id = config["configurable"]["thread_id"]
302
- parent_ts = config["configurable"].get("thread_ts")
303
-
304
- with self._get_sync_connection() as conn:
305
- with conn.cursor() as cur:
306
- cur.execute(
307
- """
308
- INSERT INTO checkpoints
309
- (thread_id, thread_ts, parent_ts, checkpoint)
310
- VALUES
311
- (%(thread_id)s, %(thread_ts)s, %(parent_ts)s, %(checkpoint)s)
312
- ON CONFLICT (thread_id, thread_ts)
313
- DO UPDATE SET checkpoint = EXCLUDED.checkpoint;
314
- """,
315
- {
316
- "thread_id": thread_id,
317
- "thread_ts": checkpoint["ts"],
318
- "parent_ts": parent_ts if parent_ts else None,
319
- "checkpoint": self.serializer.dumps(checkpoint),
320
- },
321
- )
322
-
323
- return {
324
- "configurable": {
325
- "thread_id": thread_id,
326
- "thread_ts": checkpoint["ts"],
327
- },
328
- }
329
-
330
- async def aput(
331
- self, config: RunnableConfig, checkpoint: Checkpoint
332
- ) -> RunnableConfig:
333
- """Put the checkpoint for the given configuration.
334
-
335
- Args:
336
- config: The configuration for the checkpoint.
337
- A dict with a `configurable` key which is a dict with
338
- a `thread_id` key and an optional `thread_ts` key.
339
- For example, { 'configurable': { 'thread_id': 'test_thread' } }
340
- checkpoint: The checkpoint to persist.
341
-
342
- Returns:
343
- The RunnableConfig that describes the checkpoint that was just created.
344
- It'll contain the `thread_id` and `thread_ts` of the checkpoint.
345
- """
346
- thread_id = config["configurable"]["thread_id"]
347
- parent_ts = config["configurable"].get("thread_ts")
348
- async with self._get_async_connection() as conn:
349
- async with conn.cursor() as cur:
350
- await cur.execute(
351
- """
352
- INSERT INTO
353
- checkpoints (thread_id, thread_ts, parent_ts, checkpoint)
354
- VALUES
355
- (%(thread_id)s, %(thread_ts)s, %(parent_ts)s, %(checkpoint)s)
356
- ON CONFLICT (thread_id, thread_ts)
357
- DO UPDATE SET checkpoint = EXCLUDED.checkpoint;
358
- """,
359
- {
360
- "thread_id": thread_id,
361
- "thread_ts": checkpoint["ts"],
362
- "parent_ts": parent_ts if parent_ts else None,
363
- "checkpoint": self.serializer.dumps(checkpoint),
364
- },
365
- )
366
-
367
- return {
368
- "configurable": {
369
- "thread_id": thread_id,
370
- "thread_ts": checkpoint["ts"],
371
- },
372
- }
373
-
374
- def list(self, config: RunnableConfig) -> Generator[CheckpointTuple, None, None]:
375
- """Get all the checkpoints for the given configuration."""
376
- with self._get_sync_connection() as conn:
377
- with conn.cursor() as cur:
378
- thread_id = config["configurable"]["thread_id"]
379
- cur.execute(
380
- "SELECT checkpoint, thread_ts, parent_ts "
381
- "FROM checkpoints "
382
- "WHERE thread_id = %(thread_id)s "
383
- "ORDER BY thread_ts DESC",
384
- {
385
- "thread_id": thread_id,
386
- },
387
- )
388
- for value in cur:
389
- yield CheckpointTuple(
390
- {
391
- "configurable": {
392
- "thread_id": thread_id,
393
- "thread_ts": value[1].isoformat(),
394
- }
395
- },
396
- self.serializer.loads(value[0]),
397
- {
398
- "configurable": {
399
- "thread_id": thread_id,
400
- "thread_ts": value[2].isoformat(),
401
- }
402
- }
403
- if value[2]
404
- else None,
405
- )
406
-
407
- async def alist(self, config: RunnableConfig) -> AsyncIterator[CheckpointTuple]:
408
- """Get all the checkpoints for the given configuration."""
409
- async with self._get_async_connection() as conn:
410
- async with conn.cursor() as cur:
411
- thread_id = config["configurable"]["thread_id"]
412
- await cur.execute(
413
- "SELECT checkpoint, thread_ts, parent_ts "
414
- "FROM checkpoints "
415
- "WHERE thread_id = %(thread_id)s "
416
- "ORDER BY thread_ts DESC",
417
- {
418
- "thread_id": thread_id,
419
- },
420
- )
421
- async for value in cur:
422
- yield CheckpointTuple(
423
- {
424
- "configurable": {
425
- "thread_id": thread_id,
426
- "thread_ts": value[1].isoformat(),
427
- }
428
- },
429
- self.serializer.loads(value[0]),
430
- {
431
- "configurable": {
432
- "thread_id": thread_id,
433
- "thread_ts": value[2].isoformat(),
434
- }
435
- }
436
- if value[2]
437
- else None,
438
- )
439
-
440
- def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
441
- """Get the checkpoint tuple for the given configuration.
442
-
443
- Args:
444
- config: The configuration for the checkpoint.
445
- A dict with a `configurable` key which is a dict with
446
- a `thread_id` key and an optional `thread_ts` key.
447
- For example, { 'configurable': { 'thread_id': 'test_thread' } }
448
-
449
- Returns:
450
- The checkpoint tuple for the given configuration if it exists,
451
- otherwise None.
452
-
453
- If thread_ts is None, the latest checkpoint is returned if it exists.
454
- """
455
- thread_id = config["configurable"]["thread_id"]
456
- thread_ts = config["configurable"].get("thread_ts")
457
- with self._get_sync_connection() as conn:
458
- with conn.cursor() as cur:
459
- if thread_ts:
460
- cur.execute(
461
- "SELECT checkpoint, parent_ts "
462
- "FROM checkpoints "
463
- "WHERE thread_id = %(thread_id)s AND thread_ts = %(thread_ts)s",
464
- {
465
- "thread_id": thread_id,
466
- "thread_ts": thread_ts,
467
- },
468
- )
469
- value = cur.fetchone()
470
- if value:
471
- return CheckpointTuple(
472
- config,
473
- self.serializer.loads(value[0]),
474
- {
475
- "configurable": {
476
- "thread_id": thread_id,
477
- "thread_ts": value[1].isoformat(),
478
- }
479
- }
480
- if value[1]
481
- else None,
482
- )
483
- else:
484
- cur.execute(
485
- "SELECT checkpoint, thread_ts, parent_ts "
486
- "FROM checkpoints "
487
- "WHERE thread_id = %(thread_id)s "
488
- "ORDER BY thread_ts DESC LIMIT 1",
489
- {
490
- "thread_id": thread_id,
491
- },
492
- )
493
- value = cur.fetchone()
494
- if value:
495
- return CheckpointTuple(
496
- config={
497
- "configurable": {
498
- "thread_id": thread_id,
499
- "thread_ts": value[1].isoformat(),
500
- }
501
- },
502
- checkpoint=self.serializer.loads(value[0]),
503
- parent_config={
504
- "configurable": {
505
- "thread_id": thread_id,
506
- "thread_ts": value[2].isoformat(),
507
- }
508
- }
509
- if value[2]
510
- else None,
511
- )
512
- return None
513
-
514
- async def aget_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]:
515
- """Get the checkpoint tuple for the given configuration.
516
-
517
- Args:
518
- config: The configuration for the checkpoint.
519
- A dict with a `configurable` key which is a dict with
520
- a `thread_id` key and an optional `thread_ts` key.
521
- For example, { 'configurable': { 'thread_id': 'test_thread' } }
522
-
523
- Returns:
524
- The checkpoint tuple for the given configuration if it exists,
525
- otherwise None.
526
-
527
- If thread_ts is None, the latest checkpoint is returned if it exists.
528
- """
529
- thread_id = config["configurable"]["thread_id"]
530
- thread_ts = config["configurable"].get("thread_ts")
531
- async with self._get_async_connection() as conn:
532
- async with conn.cursor() as cur:
533
- if thread_ts:
534
- await cur.execute(
535
- "SELECT checkpoint, parent_ts "
536
- "FROM checkpoints "
537
- "WHERE thread_id = %(thread_id)s AND thread_ts = %(thread_ts)s",
538
- {
539
- "thread_id": thread_id,
540
- "thread_ts": thread_ts,
541
- },
542
- )
543
- value = await cur.fetchone()
544
- if value:
545
- return CheckpointTuple(
546
- config,
547
- self.serializer.loads(value[0]),
548
- {
549
- "configurable": {
550
- "thread_id": thread_id,
551
- "thread_ts": value[1].isoformat(),
552
- }
553
- }
554
- if value[1]
555
- else None,
556
- )
557
- else:
558
- await cur.execute(
559
- "SELECT checkpoint, thread_ts, parent_ts "
560
- "FROM checkpoints "
561
- "WHERE thread_id = %(thread_id)s "
562
- "ORDER BY thread_ts DESC LIMIT 1",
563
- {
564
- "thread_id": thread_id,
565
- },
566
- )
567
- value = await cur.fetchone()
568
- if value:
569
- return CheckpointTuple(
570
- config={
571
- "configurable": {
572
- "thread_id": thread_id,
573
- "thread_ts": value[1].isoformat(),
574
- }
575
- },
576
- checkpoint=self.serializer.loads(value[0]),
577
- parent_config={
578
- "configurable": {
579
- "thread_id": thread_id,
580
- "thread_ts": value[2].isoformat(),
581
- }
582
- }
583
- if value[2]
584
- else None,
585
- )
586
-
587
- return None
@@ -1,10 +0,0 @@
1
- langchain_postgres/__init__.py,sha256=IMtNMHfY5cWhBuJ8M8diGlOD51L_4Dd741nYa_FeLsY,621
2
- langchain_postgres/_utils.py,sha256=Johm50HEgA4qScLDaSDIfJfF3DygjA5KTBtNeVcD63I,2914
3
- langchain_postgres/chat_message_histories.py,sha256=gh6hjBlrJ5GSo5kePQdh3VhiUYoWWdP37GXtZ1e25a4,14033
4
- langchain_postgres/checkpoint.py,sha256=B0c03jyQcS0bAaNPljWEG3IuhlelIF2z3vSXWATi9-4,23519
5
- langchain_postgres/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- langchain_postgres/vectorstores.py,sha256=KUeDv47lwWM88HZQJdxjtRLS_jtSzekY6FluKji7BcU,49908
7
- langchain_postgres-0.0.4.dist-info/LICENSE,sha256=2btS8uNUDWD_UNjw9ba6ZJt_00aUjEw9CGyK-xIHY8c,1072
8
- langchain_postgres-0.0.4.dist-info/METADATA,sha256=24WiDipxuAvDonZRUVhmPLdZMiTBinfzV1QaGWDs5lw,6014
9
- langchain_postgres-0.0.4.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
10
- langchain_postgres-0.0.4.dist-info/RECORD,,