From f49e8dbc98763c13e5fc76de2e4f3952d83fd224 Mon Sep 17 00:00:00 2001 From: William FH <13333726+hinthornw@users.noreply.github.com> Date: Tue, 7 May 2024 18:40:31 -0700 Subject: [PATCH] Update Checkpoints Docstrings (#418) --- docs/docs/reference/checkpoints.md | 1 + docs/mkdocs.yml | 1 + langgraph/checkpoint/aiosqlite.py | 9 +++- langgraph/checkpoint/memory.py | 4 ++ langgraph/checkpoint/sqlite.py | 86 +++++++++++++++++------------- langgraph/errors.py | 15 ++++++ 6 files changed, 77 insertions(+), 39 deletions(-) diff --git a/docs/docs/reference/checkpoints.md b/docs/docs/reference/checkpoints.md index bc653e4c2..418bd28fa 100644 --- a/docs/docs/reference/checkpoints.md +++ b/docs/docs/reference/checkpoints.md @@ -11,6 +11,7 @@ You can [compile](https://langchain-ai.github.io/langgraph/reference/graphs/#lan ::: langgraph.checkpoint.Checkpoint + ### BaseCheckpointSaver ::: langgraph.checkpoint.base.BaseCheckpointSaver diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 1ba03592f..8e2bf207e 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -155,6 +155,7 @@ nav: - Graphs: reference/graphs.md - Checkpointing: reference/checkpoints.md - Prebuilt Components: reference/prebuilt.md + - Errors: reference/errors.md markdown_extensions: diff --git a/langgraph/checkpoint/aiosqlite.py b/langgraph/checkpoint/aiosqlite.py index 35f3c209b..38fb1eb67 100644 --- a/langgraph/checkpoint/aiosqlite.py +++ b/langgraph/checkpoint/aiosqlite.py @@ -20,7 +20,14 @@ from langgraph.checkpoint.sqlite import JsonPlusSerializerCompat class AsyncSqliteSaver(BaseCheckpointSaver, AbstractAsyncContextManager): """An asynchronous checkpoint saver that stores checkpoints in a SQLite database. - Note: Requires the `aiosqlite` package. Install it with `pip install aiosqlite`. + Tip: + Requires the [aiosqlite](https://pypi.org/project/aiosqlite/) package. + Install it with `pip install aiosqlite`. + + Note: + While this class does support asynchronous checkpointing, it is not recommended + for production workloads, due to limitations in SQLite's write performance. For + production workloads, consider using a more robust database like PostgreSQL. Args: conn (aiosqlite.Connection): The asynchronous SQLite database connection. diff --git a/langgraph/checkpoint/memory.py b/langgraph/checkpoint/memory.py index 01c20e1d8..04f94afd5 100644 --- a/langgraph/checkpoint/memory.py +++ b/langgraph/checkpoint/memory.py @@ -18,6 +18,10 @@ class MemorySaver(BaseCheckpointSaver): This checkpoint saver stores checkpoints in memory using a defaultdict. + Note: + Since checkpoints are saved in memory, they will be lost when the program exits. + Only use this saver for debugging or testing purposes. + Args: serde (Optional[SerializerProtocol]): The serializer to use for serializing and deserializing checkpoints. Defaults to None. diff --git a/langgraph/checkpoint/sqlite.py b/langgraph/checkpoint/sqlite.py index 596bc2643..a9b2b39c3 100644 --- a/langgraph/checkpoint/sqlite.py +++ b/langgraph/checkpoint/sqlite.py @@ -51,8 +51,12 @@ class JsonPlusSerializerCompat(JsonPlusSerializer): class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): """A checkpoint saver that stores checkpoints in a SQLite database. - Note: While useful for demos and small projects, this class does not - scale to multiple threads. + Note: + This class is meant for lightweight, synchronous use cases + (demos and small projects) and does not + scale to multiple threads. + For a similar sqlite saver with `async` support, + consider using [AsyncSqliteSaver](#langgraph.checkpoint.aiosqlite.AsyncSqliteSaver`). Args: conn (sqlite3.Connection): The SQLite database connection. @@ -60,24 +64,24 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): Examples: - import sqlite3 + import sqlite3 - from langgraph.checkpoint.sqlite import SqliteSaver - from langgraph.graph import StateGraph + from langgraph.checkpoint.sqlite import SqliteSaver + from langgraph.graph import StateGraph - builder = StateGraph(int) - builder.add_node("add_one", lambda x: x + 1) - builder.set_entry_point("add_one") - builder.set_finish_point("add_one") - conn = sqlite3.connect("checkpoints.sqlite") - memory = SqliteSaver(conn) - graph = builder.compile(checkpointer=memory) + builder = StateGraph(int) + builder.add_node("add_one", lambda x: x + 1) + builder.set_entry_point("add_one") + builder.set_finish_point("add_one") + conn = sqlite3.connect("checkpoints.sqlite") + memory = SqliteSaver(conn) + graph = builder.compile(checkpointer=memory) - config = {"configurable": {"thread_id": "1"}} - # checkpoint = {"ts": "2023-05-03T10:00:00Z", "data": {"key": "value"}} - result = graph.invoke(3, config) - graph.get_state(config) - # Output: StateSnapshot(values=4, next=(), config={'configurable': {'thread_id': '1', 'thread_ts': '2024-05-04T06:32:42.235444+00:00'}}, parent_config=None) + config = {"configurable": {"thread_id": "1"}} + # checkpoint = {"ts": "2023-05-03T10:00:00Z", "data": {"key": "value"}} + result = graph.invoke(3, config) + graph.get_state(config) + # Output: StateSnapshot(values=4, next=(), config={'configurable': {'thread_id': '1', 'thread_ts': '2024-05-04T06:32:42.235444+00:00'}}, parent_config=None) """ # noqa serde = JsonPlusSerializerCompat() @@ -229,14 +233,16 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): config, self.serde.loads(value[0]), self.serde.loads(value[2]) if value[2] is not None else {}, - { - "configurable": { - "thread_id": config["configurable"]["thread_id"], - "thread_ts": value[1], + ( + { + "configurable": { + "thread_id": config["configurable"]["thread_id"], + "thread_ts": value[1], + } } - } - if value[1] - else None, + if value[1] + else None + ), ) else: cur.execute( @@ -253,14 +259,16 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): }, self.serde.loads(value[3]), self.serde.loads(value[4]) if value[4] is not None else {}, - { - "configurable": { - "thread_id": value[0], - "thread_ts": value[2], + ( + { + "configurable": { + "thread_id": value[0], + "thread_ts": value[2], + } } - } - if value[2] - else None, + if value[2] + else None + ), ) def list( @@ -317,14 +325,16 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): {"configurable": {"thread_id": thread_id, "thread_ts": thread_ts}}, self.serde.loads(value), self.serde.loads(metadata) if metadata is not None else {}, - { - "configurable": { - "thread_id": thread_id, - "thread_ts": parent_ts, + ( + { + "configurable": { + "thread_id": thread_id, + "thread_ts": parent_ts, + } } - } - if parent_ts - else None, + if parent_ts + else None + ), ) def put( diff --git a/langgraph/errors.py b/langgraph/errors.py index 55fd3fc96..9191437d3 100644 --- a/langgraph/errors.py +++ b/langgraph/errors.py @@ -1,4 +1,19 @@ class GraphRecursionError(RecursionError): + """Raised when the graph has exhausted the maximum number of steps. + + This prevents infinite loops. To increase the maximum number of steps, + run your graph with a config specifying a higher `recursion_limit`. + + Examples: + + graph = builder.compile() + graph.invoke( + {"messages": [("user", "Hello, world!")]}, + # The config is the second positional argument + {"recursion_limit": 1000}, + ) + """ + pass