pydantic-graph-interrupt


Namepydantic-graph-interrupt JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryInterrupt nodes for pydantic-graph
upload_time2025-10-12 22:18:55
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseNone
keywords graph interrupt pydantic pydantic-graph
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pedantic-graph-interrupt

Run interruptible Pydantic Graphs that can be interrupted and resumed.

## Table of Contents

- [pedantic-graph-interrupt](#pedantic-graph-interrupt)
  - [Table of Contents](#table-of-contents)
  - [Use cases](#use-cases)
  - [Installation](#installation)
  - [Concepts](#concepts)
  - [Usage](#usage)
    - [Define graph with interrupt nodes](#define-graph-with-interrupt-nodes)
    - [Initialize persistence](#initialize-persistence)
    - [Start the graph](#start-the-graph)

## Use cases

* LLM AI chatbots implemented using [pydantic-ai] that need to obtain user
  input outside of the graph.
* Document processing workflows where an external approval or human
  involvement is required.
* Graphs that can run for a very long time, so their execution must be
  broken into smaller continuous chunks. Most often each chunk is executed
  in a background worker, when the graph is ready to resume its run.
* And more...

## Installation

```bash
pip install pydantic-graph-interrupt
```

## Concepts

* `InterruptNode` is a special node type that interrupts the graph execution.
* `proceed()` is an alternative to `graph.run()`. It can be used to both start
  the graph run from scratch or resume it after an interruption.

## Usage

### Define graph with interrupt nodes

```python
"""graph.py"""
from dataclasses import dataclass, field
from pydantic_graph import BaseNode, End, GraphRunContext
from pydantic_graph_interrupt import InterruptibleGraph, InterruptNode, Required

@dataclass
class MyState:
    user_name: str | None = None
    messages: list[str] = field(default_factory=list)

@dataclass
class Greet(BaseNode[MyState]):
    async def run(self, ctx: GraphRunContext[MyState]) -> "WaitForName":
        ctx.state.messages.append("Hello, what is your name?")
        return WaitForName()

@dataclass  # ↓ This is an interrupt node
class WaitForName(InterruptNode[MyState]):
    # ↓ This is a required field that must be provided to resume the graph
    user_name: Annotated[str | None, Required] = None

    async def run(self, ctx: GraphRunContext[MyState]) -> "Goodbye":
        ctx.state.user_name = self.user_name
        return Goodbye(user_name=self.user_name)

@dataclass
class Goodbye(BaseNode[MyState]):
    user_name: str

    async def run(self, ctx: GraphRunContext[MyState]) -> End:
        ctx.state.messages.append(f"Goodbye, {self.user_name}!")
        return End(ctx.state)

my_graph = InterruptibleGraph(nodes=[Greet, WaitForName, Goodbye])
```

### Initialize persistence

When you just start, you have to initialize the persistence. This defines
the starting node of the graph and sets correct types inside the persistence.

```python
"""steps.py"""
from pathlib import Path
from pydantic_graph.persistence.file import FileStatePersistence
from .graph import MyState, my_graph, Greet

async def start():
    persistence = FileStatePersistence(Path("offline_state.json"))
    state = MyState()
    await my_graph.initialize(Greet(), persistence=persistence, state=state)
```

### Start the graph

```python
"""main.py"""
from .steps import start
await start()
```

[State Persistence]: https://ai.pydantic.dev/graph/#state-persistence
[pydantic-ai]: https://ai.pydantic.dev/

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pydantic-graph-interrupt",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": "vduseev <vagiz@duseev.com>",
    "keywords": "graph, interrupt, pydantic, pydantic-graph",
    "author": null,
    "author_email": "vduseev <vagiz@duseev.com>",
    "download_url": "https://files.pythonhosted.org/packages/2e/67/e4c86337f0da3b02fba0c0a0f59343e95d58bde308cddd9fe21bb8e57c34/pydantic_graph_interrupt-0.1.0.tar.gz",
    "platform": null,
    "description": "# pedantic-graph-interrupt\n\nRun interruptible Pydantic Graphs that can be interrupted and resumed.\n\n## Table of Contents\n\n- [pedantic-graph-interrupt](#pedantic-graph-interrupt)\n  - [Table of Contents](#table-of-contents)\n  - [Use cases](#use-cases)\n  - [Installation](#installation)\n  - [Concepts](#concepts)\n  - [Usage](#usage)\n    - [Define graph with interrupt nodes](#define-graph-with-interrupt-nodes)\n    - [Initialize persistence](#initialize-persistence)\n    - [Start the graph](#start-the-graph)\n\n## Use cases\n\n* LLM AI chatbots implemented using [pydantic-ai] that need to obtain user\n  input outside of the graph.\n* Document processing workflows where an external approval or human\n  involvement is required.\n* Graphs that can run for a very long time, so their execution must be\n  broken into smaller continuous chunks. Most often each chunk is executed\n  in a background worker, when the graph is ready to resume its run.\n* And more...\n\n## Installation\n\n```bash\npip install pydantic-graph-interrupt\n```\n\n## Concepts\n\n* `InterruptNode` is a special node type that interrupts the graph execution.\n* `proceed()` is an alternative to `graph.run()`. It can be used to both start\n  the graph run from scratch or resume it after an interruption.\n\n## Usage\n\n### Define graph with interrupt nodes\n\n```python\n\"\"\"graph.py\"\"\"\nfrom dataclasses import dataclass, field\nfrom pydantic_graph import BaseNode, End, GraphRunContext\nfrom pydantic_graph_interrupt import InterruptibleGraph, InterruptNode, Required\n\n@dataclass\nclass MyState:\n    user_name: str | None = None\n    messages: list[str] = field(default_factory=list)\n\n@dataclass\nclass Greet(BaseNode[MyState]):\n    async def run(self, ctx: GraphRunContext[MyState]) -> \"WaitForName\":\n        ctx.state.messages.append(\"Hello, what is your name?\")\n        return WaitForName()\n\n@dataclass  # \u2193 This is an interrupt node\nclass WaitForName(InterruptNode[MyState]):\n    # \u2193 This is a required field that must be provided to resume the graph\n    user_name: Annotated[str | None, Required] = None\n\n    async def run(self, ctx: GraphRunContext[MyState]) -> \"Goodbye\":\n        ctx.state.user_name = self.user_name\n        return Goodbye(user_name=self.user_name)\n\n@dataclass\nclass Goodbye(BaseNode[MyState]):\n    user_name: str\n\n    async def run(self, ctx: GraphRunContext[MyState]) -> End:\n        ctx.state.messages.append(f\"Goodbye, {self.user_name}!\")\n        return End(ctx.state)\n\nmy_graph = InterruptibleGraph(nodes=[Greet, WaitForName, Goodbye])\n```\n\n### Initialize persistence\n\nWhen you just start, you have to initialize the persistence. This defines\nthe starting node of the graph and sets correct types inside the persistence.\n\n```python\n\"\"\"steps.py\"\"\"\nfrom pathlib import Path\nfrom pydantic_graph.persistence.file import FileStatePersistence\nfrom .graph import MyState, my_graph, Greet\n\nasync def start():\n    persistence = FileStatePersistence(Path(\"offline_state.json\"))\n    state = MyState()\n    await my_graph.initialize(Greet(), persistence=persistence, state=state)\n```\n\n### Start the graph\n\n```python\n\"\"\"main.py\"\"\"\nfrom .steps import start\nawait start()\n```\n\n[State Persistence]: https://ai.pydantic.dev/graph/#state-persistence\n[pydantic-ai]: https://ai.pydantic.dev/\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Interrupt nodes for pydantic-graph",
    "version": "0.1.0",
    "project_urls": {
        "Documentation": "https://github.com/vduseev/pydantic-graph-interrupt",
        "Homepage": "https://github.com/vduseev/pydantic-graph-interrupt",
        "Issues": "https://github.com/vduseev/pydantic-graph-interrupt/issues",
        "Repository": "https://github.com/vduseev/pydantic-graph-interrupt"
    },
    "split_keywords": [
        "graph",
        " interrupt",
        " pydantic",
        " pydantic-graph"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "48ade34e61e86db8ab28d6106575fa663fac7887feba2159273d7916e26c526f",
                "md5": "bf5f2882942397e645ff6ef947e3ac92",
                "sha256": "4b46de6466244f83d8cbbc9c20ce6cfb584d7b80c7c2601a28b380f264f476b5"
            },
            "downloads": -1,
            "filename": "pydantic_graph_interrupt-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "bf5f2882942397e645ff6ef947e3ac92",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 13590,
            "upload_time": "2025-10-12T22:18:53",
            "upload_time_iso_8601": "2025-10-12T22:18:53.920362Z",
            "url": "https://files.pythonhosted.org/packages/48/ad/e34e61e86db8ab28d6106575fa663fac7887feba2159273d7916e26c526f/pydantic_graph_interrupt-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "2e67e4c86337f0da3b02fba0c0a0f59343e95d58bde308cddd9fe21bb8e57c34",
                "md5": "0bd86dcf268c731cdc4d7c0f23056394",
                "sha256": "fd065c197b4e3f327da49b55809e30980734f71ac7f5ac17fb87d597a3e9ac48"
            },
            "downloads": -1,
            "filename": "pydantic_graph_interrupt-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "0bd86dcf268c731cdc4d7c0f23056394",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 15264,
            "upload_time": "2025-10-12T22:18:55",
            "upload_time_iso_8601": "2025-10-12T22:18:55.325838Z",
            "url": "https://files.pythonhosted.org/packages/2e/67/e4c86337f0da3b02fba0c0a0f59343e95d58bde308cddd9fe21bb8e57c34/pydantic_graph_interrupt-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-12 22:18:55",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "vduseev",
    "github_project": "pydantic-graph-interrupt",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "pydantic-graph-interrupt"
}
        
Elapsed time: 1.55546s