diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml new file mode 100644 index 000000000..4d3497508 --- /dev/null +++ b/.github/workflows/size.yml @@ -0,0 +1,28 @@ +name: Check File Size + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + file-size-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v44 + - name: Filter by size + run: | + large_added_files=$(find ${{ steps.changed-files.outputs.added_files }} -maxdepth 0 -size +1M) + if [ -n "$large_added_files" ]; then + echo "Large files added: $large_added_files" + echo "# Large files added:" >> $GITHUB_STEP_SUMMARY + echo "$large_added_files" >> $GITHUB_STEP_SUMMARY + exit 1 + fi diff --git a/docs/docs/cloud/deployment/graph_rebuild.md b/docs/docs/cloud/deployment/graph_rebuild.md new file mode 100644 index 000000000..c7853b30a --- /dev/null +++ b/docs/docs/cloud/deployment/graph_rebuild.md @@ -0,0 +1,146 @@ +# Rebuild Graph at Runtime + +You might need to rebuild your graph with a different configuration for a new run. For example, you might need to use a different graph state or graph structure depending on the config. This guide shows how you can do this. + +!!! note "Note" + In most cases, customizing behavior based on the config should be handled by a single graph where each node can read a config and change its behavior based on it + +## Prerequisites + +Make sure to check out [this how-to guide](./setup.md) on setting up your app for deployment first. + +## Define graphs + +Let's say you have an app with a simple graph that calls an LLM and returns the response to the user. The app file directory looks like the following: + +``` +my-app/ +|-- requirements.txt +|-- .env +|-- openai_agent.py # code for your graph +``` + +where the graph is defined in `openai_agent.py`. + +### No rebuild + +In the standard LangGraph API configuration, the server uses the compiled graph instance that's defined at the top level of `openai_agent.py`, which looks like the following: + +```python +from langchain_openai import ChatOpenAI +from langgraph.graph import END, MessageGraph + +model = ChatOpenAI(temperature=0) + +graph_workflow = MessageGraph() + +graph_workflow.add_node("agent", model) +graph_workflow.add_edge("agent", END) +graph_workflow.set_entry_point("agent") + +agent = graph_workflow.compile() +``` + +To make the server aware of your graph, you need to specify a path to the variable that contains the `CompiledStateGraph` instance in your LangGraph API configuration (`langgraph.json`), e.g.: + +``` +{ + "dependencies": ["."], + "graphs": { + "openai_agent": "./openai_agent.py:agent", + }, + "env": "./.env" +} +``` + +### Rebuild + +To make your graph rebuild on each new run with custom configuration, you need to rewrite `openai_agent.py` to instead provide a _function_ that takes a config and returns a graph (or compiled graph) instance. Let's say we want to return our existing graph for user ID '1', and a tool-calling agent for other users. We can modify `openai_agent.py` as follows: + +```python +from typing import Annotated, TypedDict +from langchain_openai import ChatOpenAI +from langgraph.graph import END, MessageGraph +from langgraph.graph.state import StateGraph +from langgraph.graph.message import add_messages +from langgraph.prebuilt import ToolNode +from langchain_core.tools import tool +from langchain_core.messages import BaseMessage +from langchain_core.runnables import RunnableConfig + + +class State(TypedDict): + messages: Annotated[list[BaseMessage], add_messages] + + +model = ChatOpenAI(temperature=0) + +def make_default_graph(): + """Make a simple LLM agent""" + graph_workflow = StateGraph(State) + def call_model(state): + return {"messages": [model.invoke(state["messages"])]} + + graph_workflow.add_node("agent", call_model) + graph_workflow.add_edge("agent", END) + graph_workflow.set_entry_point("agent") + + agent = graph_workflow.compile() + return agent + + +def make_alternative_graph(): + """Make a tool-calling agent""" + + @tool + def add(a: float, b: float): + """Adds two numbers.""" + return a + b + + tool_node = ToolNode([add]) + model_with_tools = model.bind_tools([add]) + def call_model(state): + return {"messages": [model_with_tools.invoke(state["messages"])]} + + def should_continue(state: State): + if state["messages"][-1].tool_calls: + return "tools" + else: + return END + + graph_workflow = StateGraph(State) + + graph_workflow.add_node("agent", call_model) + graph_workflow.add_node("tools", tool_node) + graph_workflow.add_edge("tools", "agent") + graph_workflow.set_entry_point("agent") + graph_workflow.add_conditional_edges("agent", should_continue) + + agent = graph_workflow.compile() + return agent + + +# this is the graph making function that will decide which graph to +# build based on the provided config +def make_graph(config: RunnableConfig): + user_id = config.get("configurable", {}).get("user_id") + # route to different graph state / structure based on the user ID + if user_id == "1": + return make_default_graph() + else: + return make_alternative_graph() +``` + +Finally, you need to specify the path to your graph-making function (`make_graph`) in `langgraph.json`: + +``` +{ + "dependencies": ["."], + "graphs": { + "openai_agent": "./openai_agent.py:make_graph", + }, + "env": "./.env" +} +``` + +See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file) \ No newline at end of file diff --git a/docs/docs/cloud/deployment/setup.md b/docs/docs/cloud/deployment/setup.md index 0ab7900d0..1aa7da3f8 100644 --- a/docs/docs/cloud/deployment/setup.md +++ b/docs/docs/cloud/deployment/setup.md @@ -88,7 +88,7 @@ agent = graph_workflow.compile() ``` !!! warning "Assign `CompiledGraph` to Variable" - The build process for LangGraph Cloud requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module. + The build process for LangGraph Cloud requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module (alternatively, you can provide [a function that creates a graph](./graph_rebuild.md)). Example file directory: ``` diff --git a/docs/docs/cloud/reference/cli.md b/docs/docs/cloud/reference/cli.md index 5e77ad113..ee481ea20 100644 --- a/docs/docs/cloud/reference/cli.md +++ b/docs/docs/cloud/reference/cli.md @@ -13,7 +13,7 @@ The LangGraph CLI requires a JSON configuration file with the following keys: | Key | Description | | --- | ----------- | | `dependencies` | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following: (1) `"."`, which will look for local Python packages, (2) `pyproject.toml`, `setup.py` or `requirements.txt` in the app directory `"./local_package"`, or (3) a package name. | -| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph is defined. Example: `./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.graph.CompiledGraph`. | +| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: | | `env` | Path to `.env` file or a mapping from environment variable to its value. | | `python_version` | `3.11` or `3.12`. Defaults to `3.11`. | | `pip_config_file`| Path to `pip` config file. | @@ -49,7 +49,7 @@ Example: "." ], "graphs": { - "my_graph_id": "./your_package/your_file.py:variable" + "my_graph_id": "./your_package/your_file.py:make_graph" }, "env": { "OPENAI_API_KEY": "secret-key" diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index b1783e492..b5cedfc97 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -61,6 +61,13 @@ These guides show how to use different streaming modes. - [How to pass graph state to tools](pass-run-time-values-to-tools.ipynb) - [How to pass config to tools](pass-config-to-tools.ipynb) +## State Management + +- [Use Pydantic model as state](state-model.ipynb) +- [Use a context object in state](state-context-key.ipynb) +- [Have a separate input and output schema](input_output_schema.ipynb) +- [Pass private state between nodes inside the graph](pass_private_state.ipynb) + ## Other - [How to run graph asynchronously](async.ipynb) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 083661975..a3bfb34e7 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -192,6 +192,7 @@ nav: - Deployment: - Setup App: "cloud/deployment/setup.md" - Setup App (pyproject.toml): "cloud/deployment/setup_pyproject.md" + - Rebuild Graph at Runtime: "cloud/deployment/graph_rebuild.md" - Test App Locally: "cloud/deployment/test_locally.md" - Deploy to Cloud: "cloud/deployment/cloud.md" - Self-Host: "cloud/deployment/self_hosted.md" diff --git a/examples/chatbot-simulation-evaluation/img/virtual_user_diagram.png b/examples/chatbot-simulation-evaluation/img/virtual_user_diagram.png index a0cefb06b..716257274 100644 Binary files a/examples/chatbot-simulation-evaluation/img/virtual_user_diagram.png and b/examples/chatbot-simulation-evaluation/img/virtual_user_diagram.png differ diff --git a/examples/chatbots/imgs/prompt-generator.png b/examples/chatbots/imgs/prompt-generator.png index 6bcc7083f..9f547f2f9 100644 Binary files a/examples/chatbots/imgs/prompt-generator.png and b/examples/chatbots/imgs/prompt-generator.png differ diff --git a/examples/cloud_examples/img/webhook_results.png b/examples/cloud_examples/img/webhook_results.png index c0fea310a..c0df3e3e7 100644 Binary files a/examples/cloud_examples/img/webhook_results.png and b/examples/cloud_examples/img/webhook_results.png differ diff --git a/examples/cloud_examples/langgraph_to_langgraph_cloud.ipynb b/examples/cloud_examples/langgraph_to_langgraph_cloud.ipynb index 20011991b..41a174f33 100644 --- a/examples/cloud_examples/langgraph_to_langgraph_cloud.ipynb +++ b/examples/cloud_examples/langgraph_to_langgraph_cloud.ipynb @@ -50,7 +50,7 @@ "metadata": {}, "outputs": [ { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ "OPENAI_API_KEY: ········\n" @@ -991,7 +991,7 @@ "id": "08996d90-a3ff-4655-9763-1dd4971344d4", "metadata": {}, "source": [ - "### With LangGraph Clound" + "### With LangGraph Cloud" ] }, { diff --git a/examples/customer-support/img/customer-support-bot-4.png b/examples/customer-support/img/customer-support-bot-4.png index 021d8996c..f00a923ec 100644 Binary files a/examples/customer-support/img/customer-support-bot-4.png and b/examples/customer-support/img/customer-support-bot-4.png differ diff --git a/examples/customer-support/img/part-1-diagram.png b/examples/customer-support/img/part-1-diagram.png index b7d5e105e..2b4d68013 100644 Binary files a/examples/customer-support/img/part-1-diagram.png and b/examples/customer-support/img/part-1-diagram.png differ diff --git a/examples/customer-support/img/part-2-diagram.png b/examples/customer-support/img/part-2-diagram.png index 1659030bc..cf7fbee5d 100644 Binary files a/examples/customer-support/img/part-2-diagram.png and b/examples/customer-support/img/part-2-diagram.png differ diff --git a/examples/customer-support/img/part-3-diagram.png b/examples/customer-support/img/part-3-diagram.png index 7b9dbc067..f31ed7730 100644 Binary files a/examples/customer-support/img/part-3-diagram.png and b/examples/customer-support/img/part-3-diagram.png differ diff --git a/examples/customer-support/img/part-4-diagram.png b/examples/customer-support/img/part-4-diagram.png index d5d2e2e89..4449e1bb8 100644 Binary files a/examples/customer-support/img/part-4-diagram.png and b/examples/customer-support/img/part-4-diagram.png differ diff --git a/examples/lats/img/lats.png b/examples/lats/img/lats.png index 79e96a304..f9a2f6e39 100644 Binary files a/examples/lats/img/lats.png and b/examples/lats/img/lats.png differ diff --git a/examples/lats/img/tree.png b/examples/lats/img/tree.png index 6b3dd0c02..ef4b4ddcd 100644 Binary files a/examples/lats/img/tree.png and b/examples/lats/img/tree.png differ diff --git a/examples/llm-compiler/img/diagram.png b/examples/llm-compiler/img/diagram.png index 01655a5ec..0bcf9f685 100644 Binary files a/examples/llm-compiler/img/diagram.png and b/examples/llm-compiler/img/diagram.png differ diff --git a/examples/llm-compiler/img/llm-compiler.png b/examples/llm-compiler/img/llm-compiler.png index 3c9ec7a57..eb08c72b7 100644 Binary files a/examples/llm-compiler/img/llm-compiler.png and b/examples/llm-compiler/img/llm-compiler.png differ diff --git a/examples/multi_agent/img/hierarchical-diagram.png b/examples/multi_agent/img/hierarchical-diagram.png index b3fa81275..48c2eb627 100644 Binary files a/examples/multi_agent/img/hierarchical-diagram.png and b/examples/multi_agent/img/hierarchical-diagram.png differ diff --git a/examples/multi_agent/img/simple_multi_agent_diagram.png b/examples/multi_agent/img/simple_multi_agent_diagram.png index 3da47ba9b..b82be5ce9 100644 Binary files a/examples/multi_agent/img/simple_multi_agent_diagram.png and b/examples/multi_agent/img/simple_multi_agent_diagram.png differ diff --git a/examples/multi_agent/img/supervisor-diagram.png b/examples/multi_agent/img/supervisor-diagram.png index 84497aa4c..f3e92ba40 100644 Binary files a/examples/multi_agent/img/supervisor-diagram.png and b/examples/multi_agent/img/supervisor-diagram.png differ diff --git a/examples/plan-and-execute/img/plan-and-execute.png b/examples/plan-and-execute/img/plan-and-execute.png index 829f2aee8..fc068a8ba 100644 Binary files a/examples/plan-and-execute/img/plan-and-execute.png and b/examples/plan-and-execute/img/plan-and-execute.png differ diff --git a/examples/reflection/img/reflection.png b/examples/reflection/img/reflection.png index b3fdc455f..fecdc8734 100644 Binary files a/examples/reflection/img/reflection.png and b/examples/reflection/img/reflection.png differ diff --git a/examples/reflexion/img/reflexion.png b/examples/reflexion/img/reflexion.png index 929be2e64..bf319d021 100644 Binary files a/examples/reflexion/img/reflexion.png and b/examples/reflexion/img/reflexion.png differ diff --git a/examples/rewoo/img/rewoo-paper-workflow.png b/examples/rewoo/img/rewoo-paper-workflow.png index cdfc7261f..598d694c2 100644 Binary files a/examples/rewoo/img/rewoo-paper-workflow.png and b/examples/rewoo/img/rewoo-paper-workflow.png differ diff --git a/examples/rewoo/img/rewoo.png b/examples/rewoo/img/rewoo.png index 48af25d57..af295a3a6 100644 Binary files a/examples/rewoo/img/rewoo.png and b/examples/rewoo/img/rewoo.png differ diff --git a/examples/storm/img/storm.png b/examples/storm/img/storm.png index 696592181..bca442f5e 100644 Binary files a/examples/storm/img/storm.png and b/examples/storm/img/storm.png differ diff --git a/examples/subgraph.ipynb b/examples/subgraph.ipynb index cbc582165..a2b4bec3a 100644 --- a/examples/subgraph.ipynb +++ b/examples/subgraph.ipynb @@ -11,7 +11,7 @@ "source": [ "# How to create subgraphs\n", "\n", - "For more complex systems, sub-graphs are a useful design principle. Sub-graphs allow you to create and manage different states in different parts of your graph. This allows you build things like [multi-agent teams](./multi_agent/hierarchical_agent_teams.ipynb), where each team can track its own separate state.\n", + "For more complex systems, sub-graphs are a useful design principle. Sub-graphs allow you to create and manage different states in different parts of your graph. This allows you build things like [multi-agent teams](https://langchain-ai.github.io/langgraph/tutorials/multi_agent/hierarchical_agent_teams/), where each team can track its own separate state.\n", "\n", "![Screenshot 2024-07-11 at 1.01.28 PM.png](attachment:71516aef-9c00-4730-a676-a54e90cb6472.png)" ] diff --git a/examples/tutorials/tnt-llm/img/tnt_llm.png b/examples/tutorials/tnt-llm/img/tnt_llm.png index dbe96813e..c452ab789 100644 Binary files a/examples/tutorials/tnt-llm/img/tnt_llm.png and b/examples/tutorials/tnt-llm/img/tnt_llm.png differ diff --git a/examples/usaco/img/benchmark.png b/examples/usaco/img/benchmark.png index a6199631a..f0b9169a9 100644 Binary files a/examples/usaco/img/benchmark.png and b/examples/usaco/img/benchmark.png differ diff --git a/examples/usaco/img/diagram-part-1.png b/examples/usaco/img/diagram-part-1.png index f002232c6..e2a373479 100644 Binary files a/examples/usaco/img/diagram-part-1.png and b/examples/usaco/img/diagram-part-1.png differ diff --git a/examples/usaco/img/diagram-part-2.png b/examples/usaco/img/diagram-part-2.png index 0bb6ca168..54e9e2ac8 100644 Binary files a/examples/usaco/img/diagram-part-2.png and b/examples/usaco/img/diagram-part-2.png differ diff --git a/examples/usaco/img/diagram.png b/examples/usaco/img/diagram.png index 125f8cdbe..90091aa88 100644 Binary files a/examples/usaco/img/diagram.png and b/examples/usaco/img/diagram.png differ diff --git a/examples/usaco/img/usaco.png b/examples/usaco/img/usaco.png index b15be575e..47dfd393c 100644 Binary files a/examples/usaco/img/usaco.png and b/examples/usaco/img/usaco.png differ diff --git a/examples/web-navigation/img/web-voyager.excalidraw.png b/examples/web-navigation/img/web-voyager.excalidraw.png index 54563b056..32fa6555e 100644 Binary files a/examples/web-navigation/img/web-voyager.excalidraw.png and b/examples/web-navigation/img/web-voyager.excalidraw.png differ diff --git a/libs/langgraph/langgraph/pregel/__init__.py b/libs/langgraph/langgraph/pregel/__init__.py index c0edcacc3..c990b4dc6 100644 --- a/libs/langgraph/langgraph/pregel/__init__.py +++ b/libs/langgraph/langgraph/pregel/__init__.py @@ -722,8 +722,10 @@ class Pregel( if config and config.get("configurable", {}).get(CONFIG_KEY_READ) is not None: # if being called as a node in another graph, always use values mode stream_mode = ["values"] - if config is not None and config.get("configurable", {}).get( - CONFIG_KEY_CHECKPOINTER + if ( + config is not None + and config.get("configurable", {}).get(CONFIG_KEY_CHECKPOINTER) + and (interrupt_after or interrupt_before) ): checkpointer: Optional[BaseCheckpointSaver] = config["configurable"][ CONFIG_KEY_CHECKPOINTER diff --git a/libs/langgraph/langgraph/pregel/algo.py b/libs/langgraph/langgraph/pregel/algo.py index 13ce7e183..d94d4e166 100644 --- a/libs/langgraph/langgraph/pregel/algo.py +++ b/libs/langgraph/langgraph/pregel/algo.py @@ -257,6 +257,9 @@ def prepare_next_tasks( if not isinstance(packet, Send): logger.warn(f"Ignoring invalid packet type {type(packet)} in pending sends") continue + if packet.node not in processes: + logger.warn(f"Ignoring unknown node name {packet.node} in pending sends") + continue if for_execution: proc = processes[packet.node] if node := proc.get_node(): diff --git a/libs/langgraph/langgraph/pregel/loop.py b/libs/langgraph/langgraph/pregel/loop.py index ae2ba2edf..8373a1fba 100644 --- a/libs/langgraph/langgraph/pregel/loop.py +++ b/libs/langgraph/langgraph/pregel/loop.py @@ -102,6 +102,7 @@ class PregelLoop: checkpoint_pending_writes: List[PendingWrite] step: int + stop: int status: Literal[ "pending", "done", "interrupt_before", "interrupt_after", "out_of_steps" ] @@ -203,7 +204,7 @@ class PregelLoop: return False # check if iteration limit is reached - if self.step > self.config["recursion_limit"]: + if self.step > self.stop: self.status = "out_of_steps" return False @@ -419,6 +420,7 @@ class SyncPregelLoop(PregelLoop, ContextManager): ) self.status = "pending" self.step = self.checkpoint_metadata["step"] + 1 + self.stop = self.step + self.config["recursion_limit"] + 1 return self @@ -497,6 +499,7 @@ class AsyncPregelLoop(PregelLoop, AsyncContextManager): ) self.status = "pending" self.step = self.checkpoint_metadata["step"] + 1 + self.stop = self.step + self.config["recursion_limit"] + 1 return self diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index a856e7f69..d0246b257 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langgraph" -version = "0.1.12" +version = "0.1.14" description = "Building stateful, multi-actor applications with LLMs" authors = [] license = "MIT" diff --git a/libs/langgraph/tests/memory_assert.py b/libs/langgraph/tests/memory_assert.py index c0ede2f20..09c9e52af 100644 --- a/libs/langgraph/tests/memory_assert.py +++ b/libs/langgraph/tests/memory_assert.py @@ -7,6 +7,7 @@ from langchain_core.runnables import RunnableConfig from langgraph.checkpoint.base import ( Checkpoint, CheckpointMetadata, + CheckpointTuple, SerializerProtocol, copy_checkpoint, ) @@ -119,3 +120,11 @@ class MemorySaverAssertCheckpointMetadata(MemorySaver): return await asyncio.get_running_loop().run_in_executor( None, self.put, config, checkpoint, metadata ) + + +class MemorySaverNoPending(MemorySaver): + def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]: + result = super().get_tuple(config) + if result: + return CheckpointTuple(result.config, result.checkpoint, result.metadata) + return result diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index db902eca0..2a701f034 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -66,6 +66,7 @@ from tests.any_str import AnyStr from tests.memory_assert import ( MemorySaverAssertCheckpointMetadata, MemorySaverAssertImmutable, + MemorySaverNoPending, NoopSerializer, ) @@ -1149,10 +1150,32 @@ def test_cond_edge_after_send() -> None: builder.add_conditional_edges("1", send_for_fun) builder.add_conditional_edges("2", route_to_three) graph = builder.compile() - assert graph.invoke(["0"]) == ["0", "1", "2", "3"] +async def test_checkpointer_null_pending_writes() -> None: + class Node: + def __init__(self, name: str): + self.name = name + setattr(self, "__name__", name) + + def __call__(self, state): + return [self.name] + + builder = StateGraph(Annotated[list, operator.add]) + builder.add_node(Node("1")) + builder.add_edge(START, "1") + graph = builder.compile(checkpointer=MemorySaverNoPending()) + assert graph.invoke([], {"configurable": {"thread_id": "foo"}}) == ["1"] + assert graph.invoke([], {"configurable": {"thread_id": "foo"}}) == ["1"] * 2 + assert (await graph.ainvoke([], {"configurable": {"thread_id": "foo"}})) == [ + "1" + ] * 3 + assert (await graph.ainvoke([], {"configurable": {"thread_id": "foo"}})) == [ + "1" + ] * 4 + + def test_invoke_checkpoint_sqlite(mocker: MockerFixture) -> None: adder = mocker.Mock(side_effect=lambda x: x["total"] + x["input"]) @@ -8570,6 +8593,7 @@ def test_nested_graph_interrupts_parallel(checkpointer: BaseCheckpointSaver) -> checkpointer.__exit__(None, None, None) +@pytest.mark.skip @pytest.mark.parametrize( "checkpointer", [ diff --git a/libs/langgraph/tests/test_pregel_async.py b/libs/langgraph/tests/test_pregel_async.py index c3f8bb3bf..84d597c43 100644 --- a/libs/langgraph/tests/test_pregel_async.py +++ b/libs/langgraph/tests/test_pregel_async.py @@ -7099,6 +7099,7 @@ async def test_nested_graph_interrupts_parallel( await checkpointer.__aexit__(None, None, None) +@pytest.mark.skip @pytest.mark.parametrize( "checkpointer", [