langgraph-checkpoint-objectstorage 0.1.7__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.
- langgraph_checkpoint_objectstorage-0.1.7/.github/CODEOWNERS +5 -0
- langgraph_checkpoint_objectstorage-0.1.7/.github/ISSUE_TEMPLATE/bug_report.yml +51 -0
- langgraph_checkpoint_objectstorage-0.1.7/.github/pull_request_template.md +19 -0
- langgraph_checkpoint_objectstorage-0.1.7/.github/workflows/ci.yml +112 -0
- langgraph_checkpoint_objectstorage-0.1.7/.github/workflows/publish.yml +42 -0
- langgraph_checkpoint_objectstorage-0.1.7/.gitignore +219 -0
- langgraph_checkpoint_objectstorage-0.1.7/.python-version +1 -0
- langgraph_checkpoint_objectstorage-0.1.7/CHANGELOG.md +89 -0
- langgraph_checkpoint_objectstorage-0.1.7/LICENSE +21 -0
- langgraph_checkpoint_objectstorage-0.1.7/Makefile +118 -0
- langgraph_checkpoint_objectstorage-0.1.7/PKG-INFO +360 -0
- langgraph_checkpoint_objectstorage-0.1.7/README.md +342 -0
- langgraph_checkpoint_objectstorage-0.1.7/docker-compose.yaml +27 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/pip/.gitignore +2 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/pip/README.md +8 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/pip/main.py +23 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/pip/requirements.txt +3 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/filesystem/.gitignore +2 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/filesystem/README.md +6 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/filesystem/main.py +23 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/filesystem/poetry.lock +1590 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/filesystem/pyproject.toml +16 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs/.gitignore +1 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs/README.md +24 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs/main.py +104 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs/poetry.lock +2978 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs/pyproject.toml +19 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs-async/.gitignore +1 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs-async/README.md +22 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs-async/main.py +85 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs-async/poetry.lock +2978 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/poetry/gcs-async/pyproject.toml +19 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/filesystem/.gitignore +2 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/filesystem/README.md +5 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/filesystem/main.py +23 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/filesystem/pyproject.toml +11 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/filesystem/uv.lock +1192 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3/.gitignore +1 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3/README.md +28 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3/main.py +101 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3/pyproject.toml +13 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3/uv.lock +1938 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3-async/.gitignore +1 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3-async/README.md +26 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3-async/main.py +84 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3-async/pyproject.toml +13 -0
- langgraph_checkpoint_objectstorage-0.1.7/examples/uv/s3-async/uv.lock +1938 -0
- langgraph_checkpoint_objectstorage-0.1.7/pyproject.toml +45 -0
- langgraph_checkpoint_objectstorage-0.1.7/src/langgraph_checkpoint_objectstorage/__init__.py +5 -0
- langgraph_checkpoint_objectstorage-0.1.7/src/langgraph_checkpoint_objectstorage/envelope.py +53 -0
- langgraph_checkpoint_objectstorage-0.1.7/src/langgraph_checkpoint_objectstorage/keys.py +49 -0
- langgraph_checkpoint_objectstorage-0.1.7/src/langgraph_checkpoint_objectstorage/py.typed +0 -0
- langgraph_checkpoint_objectstorage-0.1.7/src/langgraph_checkpoint_objectstorage/saver.py +478 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/conftest.py +108 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/integration/test_conformance_gcs.py +33 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/integration/test_conformance_local.py +18 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/integration/test_conformance_s3.py +23 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/integration/test_integration_gcs.py +27 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/integration/test_integration_s3.py +25 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/integration/test_sync_repeated_calls.py +83 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/unit/test_envelope.py +39 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/unit/test_keys.py +44 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/unit/test_logging.py +50 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/unit/test_saver_core.py +140 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/unit/test_saver_public_api.py +156 -0
- langgraph_checkpoint_objectstorage-0.1.7/tests/unit/test_typechecking.py +64 -0
- langgraph_checkpoint_objectstorage-0.1.7/uv.lock +3527 -0
- langgraph_checkpoint_objectstorage-0.1.7/vulture_whitelist.py +25 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a problem with langgraph-checkpoint-objectstorage
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
description: What did you expect instead?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: repro
|
|
15
|
+
attributes:
|
|
16
|
+
label: Steps to reproduce
|
|
17
|
+
description: A minimal code snippet, if possible
|
|
18
|
+
render: python
|
|
19
|
+
validations:
|
|
20
|
+
required: false
|
|
21
|
+
|
|
22
|
+
- type: input
|
|
23
|
+
id: backend
|
|
24
|
+
attributes:
|
|
25
|
+
label: Backend
|
|
26
|
+
description: local filesystem / S3 / GCS (and fsspec/s3fs/gcsfs version, if relevant)
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
|
|
30
|
+
- type: input
|
|
31
|
+
id: version
|
|
32
|
+
attributes:
|
|
33
|
+
label: langgraph-checkpoint-objectstorage version
|
|
34
|
+
validations:
|
|
35
|
+
required: true
|
|
36
|
+
|
|
37
|
+
- type: input
|
|
38
|
+
id: python-version
|
|
39
|
+
attributes:
|
|
40
|
+
label: Python version
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
|
|
44
|
+
- type: textarea
|
|
45
|
+
id: logs
|
|
46
|
+
attributes:
|
|
47
|
+
label: Relevant logs
|
|
48
|
+
description: Set `LANGGRAPH_CHECKPOINT_OBJECTSTORAGE_LOG_LEVEL=DEBUG` for detail (see the README's Logging section)
|
|
49
|
+
render: shell
|
|
50
|
+
validations:
|
|
51
|
+
required: false
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!-- What does this change do, and why? -->
|
|
4
|
+
|
|
5
|
+
## Type of change
|
|
6
|
+
|
|
7
|
+
- [ ] Bug fix
|
|
8
|
+
- [ ] New feature
|
|
9
|
+
- [ ] Breaking change
|
|
10
|
+
- [ ] Docs / chore
|
|
11
|
+
|
|
12
|
+
## Testing
|
|
13
|
+
|
|
14
|
+
<!-- How did you verify this? -->
|
|
15
|
+
|
|
16
|
+
## Checklist
|
|
17
|
+
|
|
18
|
+
- [ ] `make test` passes (`make test-integration` too, if this touches backend I/O)
|
|
19
|
+
- [ ] Added an entry under `## [Unreleased]` in `CHANGELOG.md` (if user-facing)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
if: "!startsWith(github.event.head_commit.message, 'chore(release):')"
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/setup-uv@v3
|
|
19
|
+
with:
|
|
20
|
+
enable-cache: true
|
|
21
|
+
- run: make lint
|
|
22
|
+
|
|
23
|
+
test-unit:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
if: "!startsWith(github.event.head_commit.message, 'chore(release):')"
|
|
26
|
+
strategy:
|
|
27
|
+
fail-fast: false
|
|
28
|
+
matrix:
|
|
29
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
30
|
+
env:
|
|
31
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: astral-sh/setup-uv@v3
|
|
35
|
+
with:
|
|
36
|
+
enable-cache: true
|
|
37
|
+
- run: make test-unit
|
|
38
|
+
|
|
39
|
+
test-integration:
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
needs: lint
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
- uses: astral-sh/setup-uv@v3
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
- run: make test-integration
|
|
48
|
+
- if: always()
|
|
49
|
+
run: docker compose down
|
|
50
|
+
|
|
51
|
+
release:
|
|
52
|
+
needs: [lint, test-unit, test-integration]
|
|
53
|
+
if: github.ref == 'refs/heads/main'
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
permissions:
|
|
56
|
+
contents: write
|
|
57
|
+
steps:
|
|
58
|
+
- name: Checkout
|
|
59
|
+
uses: actions/checkout@v4
|
|
60
|
+
with:
|
|
61
|
+
token: ${{ secrets.RELEASE_PAT }}
|
|
62
|
+
- uses: astral-sh/setup-uv@v3
|
|
63
|
+
with:
|
|
64
|
+
enable-cache: true
|
|
65
|
+
- name: Configure git identity
|
|
66
|
+
run: |
|
|
67
|
+
git config user.name "github-actions[bot]"
|
|
68
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
69
|
+
- name: Bump version and update CHANGELOG.md
|
|
70
|
+
run: |
|
|
71
|
+
python3 - <<'EOF'
|
|
72
|
+
import os
|
|
73
|
+
import re
|
|
74
|
+
from datetime import datetime, timezone
|
|
75
|
+
|
|
76
|
+
# -- bump pyproject.toml --
|
|
77
|
+
pyproject_path = "pyproject.toml"
|
|
78
|
+
text = open(pyproject_path).read()
|
|
79
|
+
match = re.search(r'^version = "(\d+)\.(\d+)\.(\d+)"$', text, re.MULTILINE)
|
|
80
|
+
major, minor, patch = match.groups()
|
|
81
|
+
new_version = f"{major}.{minor}.{int(patch) + 1}"
|
|
82
|
+
text = text[: match.start()] + f'version = "{new_version}"' + text[match.end() :]
|
|
83
|
+
open(pyproject_path, "w").write(text)
|
|
84
|
+
|
|
85
|
+
# -- move [Unreleased]'s content into a new dated version section --
|
|
86
|
+
# Contributors add entries under "## [Unreleased]" as they work
|
|
87
|
+
# (Keep a Changelog convention); release just retitles that
|
|
88
|
+
# section with the version and date, and leaves a fresh empty
|
|
89
|
+
# Unreleased section above it.
|
|
90
|
+
changelog_path = "CHANGELOG.md"
|
|
91
|
+
changelog = open(changelog_path).read()
|
|
92
|
+
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
93
|
+
marker = "## [Unreleased]"
|
|
94
|
+
start = changelog.index(marker)
|
|
95
|
+
body_start = start + len(marker)
|
|
96
|
+
next_heading = changelog.find("\n## [", body_start)
|
|
97
|
+
body = changelog[body_start:next_heading].strip("\n")
|
|
98
|
+
if not body:
|
|
99
|
+
body = "### Changed\n\n- No changelog entries were added for this release."
|
|
100
|
+
new_section = f"{marker}\n\n## [{new_version}] - {today}\n\n{body}\n\n"
|
|
101
|
+
changelog = changelog[:start] + new_section + changelog[next_heading + 1 :]
|
|
102
|
+
open(changelog_path, "w").write(changelog)
|
|
103
|
+
|
|
104
|
+
with open(os.environ["GITHUB_ENV"], "a") as f:
|
|
105
|
+
f.write(f"NEW_VERSION={new_version}\n")
|
|
106
|
+
EOF
|
|
107
|
+
- name: Commit version bump
|
|
108
|
+
run: |
|
|
109
|
+
git add pyproject.toml CHANGELOG.md
|
|
110
|
+
git commit -m "chore(release): v${NEW_VERSION}"
|
|
111
|
+
- name: Push version bump
|
|
112
|
+
run: git push origin main
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.x"
|
|
17
|
+
|
|
18
|
+
- name: Build package
|
|
19
|
+
run: |
|
|
20
|
+
python -m pip install --upgrade build
|
|
21
|
+
python -m build
|
|
22
|
+
|
|
23
|
+
- uses: actions/upload-artifact@v4
|
|
24
|
+
with:
|
|
25
|
+
name: python-package-distributions
|
|
26
|
+
path: dist/
|
|
27
|
+
|
|
28
|
+
publish:
|
|
29
|
+
needs: build
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment: pypi
|
|
32
|
+
permissions:
|
|
33
|
+
id-token: write
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: python-package-distributions
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
- name: Publish to PyPI
|
|
42
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
CLAUDE.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
Versioning is `MAJOR.MINOR.PATCH`; the patch number bumps automatically on
|
|
7
|
+
every merge to `main` (see the `release` job in `.github/workflows/ci.yml`,
|
|
8
|
+
which also updates this file).
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
## [0.1.7] - 2026-08-30
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- No changelog entries were added for this release.
|
|
17
|
+
|
|
18
|
+
## [0.1.6] - 2026-08-30
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- No changelog entries were added for this release.
|
|
23
|
+
|
|
24
|
+
## [0.1.5] - 2026-08-16
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- No changelog entries were added for this release.
|
|
29
|
+
|
|
30
|
+
## [0.1.4] - 2026-08-16
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- No changelog entries were added for this release.
|
|
35
|
+
|
|
36
|
+
## [0.1.3] - 2026-08-16
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- Usage examples for `pip`, `uv`, and `poetry` (`examples/`): a minimal
|
|
41
|
+
local-filesystem quickstart for each, plus multi-session S3 (`uv`) and
|
|
42
|
+
GCS (`poetry`) examples demonstrating sequential sessions with resume,
|
|
43
|
+
and the same pattern run concurrently via the async API.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- The sync API (`put`, `get_tuple`, `list`, `put_writes`, `delete_thread`)
|
|
48
|
+
could break on the second call against S3/GCS with `RuntimeError: Event
|
|
49
|
+
loop is closed`. `asyncio.run()` opened a fresh event loop per call, but
|
|
50
|
+
`s3fs`/`gcsfs` bind their session to whichever loop is running at first
|
|
51
|
+
use. Fixed by routing sync calls through the filesystem's own persistent
|
|
52
|
+
background loop, and giving every `ObjectStorageSaver` its own
|
|
53
|
+
filesystem instance (`skip_instance_cache=True`) so separate savers can
|
|
54
|
+
no longer share (and cross-contaminate) a session.
|
|
55
|
+
|
|
56
|
+
## [0.1.2] - 2026-08-16
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- The automated release job's version tag never reached the remote:
|
|
61
|
+
`git push --follow-tags` only pushes *annotated* tags, and the tag was
|
|
62
|
+
created lightweight. Switched to an annotated tag pushed explicitly.
|
|
63
|
+
|
|
64
|
+
## [0.1.1] - 2026-08-16
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- GitHub Actions CI (`.github/workflows/ci.yml`): lint, unit tests across
|
|
69
|
+
Python 3.11/3.12/3.13, integration tests against docker-compose S3/GCS
|
|
70
|
+
emulators, and an automated release job that bumps the patch version,
|
|
71
|
+
tags, and publishes a GitHub Release with the built wheel/sdist attached.
|
|
72
|
+
- `LICENSE` (MIT).
|
|
73
|
+
|
|
74
|
+
## [0.1.0] - 2026-08-16
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- Initial release: `ObjectStorageSaver`, a LangGraph `BaseCheckpointSaver`
|
|
79
|
+
backed by local filesystem, Google Cloud Storage, or AWS S3 through a
|
|
80
|
+
single fsspec-backed class, chosen by connection string.
|
|
81
|
+
- Full sync and async API (`get_tuple`/`aget_tuple`, `put`/`aput`,
|
|
82
|
+
`list`/`alist`, `put_writes`/`aput_writes`, `delete_thread`/`adelete_thread`),
|
|
83
|
+
validated against the official `langgraph-checkpoint-conformance` suite
|
|
84
|
+
on all three backends.
|
|
85
|
+
- Runtime type checking on the public API via `typeguard`.
|
|
86
|
+
- `py.typed` static type coverage.
|
|
87
|
+
- `Makefile` (`lint`, `test`, `test-unit`, `test-integration`, `compose-up`/
|
|
88
|
+
`compose-down`, `build`) and a `docker-compose.yaml` for local S3/GCS
|
|
89
|
+
emulators (`moto-server`, `fake-gcs-server`).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sergio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
.DEFAULT_GOAL := help
|
|
2
|
+
SHELL := /bin/bash
|
|
3
|
+
|
|
4
|
+
# Every target runs through `uv` -- never assumes an activated venv or a
|
|
5
|
+
# particular shell state. `uv run`/`uv sync` manage an isolated .venv
|
|
6
|
+
# regardless of what's on the caller's PATH.
|
|
7
|
+
UV := uv
|
|
8
|
+
REQUIRED_PYTHON := $(shell cat .python-version 2>/dev/null)
|
|
9
|
+
|
|
10
|
+
BOLD := \033[1m
|
|
11
|
+
GREEN := \033[32m
|
|
12
|
+
YELLOW := \033[33m
|
|
13
|
+
RED := \033[31m
|
|
14
|
+
CYAN := \033[36m
|
|
15
|
+
RESET := \033[0m
|
|
16
|
+
|
|
17
|
+
.PHONY: help
|
|
18
|
+
help: ## Show this help
|
|
19
|
+
@echo -e "$(BOLD)langgraph-checkpoint-objectstorage$(RESET)"
|
|
20
|
+
@echo ""
|
|
21
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
|
|
22
|
+
awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-18s$(RESET) %s\n", $$1, $$2}'
|
|
23
|
+
|
|
24
|
+
.PHONY: check-uv
|
|
25
|
+
check-uv: ## Verify uv is installed (prompts to install if missing)
|
|
26
|
+
@if command -v uv >/dev/null 2>&1; then \
|
|
27
|
+
echo -e "$(GREEN)uv found:$(RESET) $$(uv --version)"; \
|
|
28
|
+
else \
|
|
29
|
+
echo -e "$(YELLOW)uv not found on PATH.$(RESET)"; \
|
|
30
|
+
read -p "Install uv now via the official installer (curl | sh)? [y/N] " ans; \
|
|
31
|
+
if [ "$$ans" = "y" ] || [ "$$ans" = "Y" ]; then \
|
|
32
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh; \
|
|
33
|
+
echo -e "$(YELLOW)uv installed -- open a new shell (or re-source your profile) so it's on PATH, then re-run make.$(RESET)"; \
|
|
34
|
+
exit 1; \
|
|
35
|
+
else \
|
|
36
|
+
echo -e "$(RED)uv is required. Install manually: https://docs.astral.sh/uv/getting-started/installation/$(RESET)"; \
|
|
37
|
+
exit 1; \
|
|
38
|
+
fi; \
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
.PHONY: check-python
|
|
42
|
+
check-python: check-uv ## Verify a valid Python toolchain is resolvable for this repo
|
|
43
|
+
@echo "Required Python (.python-version): $(REQUIRED_PYTHON)"
|
|
44
|
+
@if resolved=$$(uv python find $(REQUIRED_PYTHON) 2>/dev/null); then \
|
|
45
|
+
echo -e "$(GREEN)Resolved:$(RESET) $$resolved"; \
|
|
46
|
+
else \
|
|
47
|
+
echo -e "$(YELLOW)Python $(REQUIRED_PYTHON) not installed locally -- uv will download it automatically on the next sync.$(RESET)"; \
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
.PHONY: install
|
|
51
|
+
install: check-python ## Install package + s3/gcs extras + dev/test toolchain into an isolated .venv
|
|
52
|
+
@$(UV) sync --all-extras --group dev
|
|
53
|
+
|
|
54
|
+
.PHONY: lint
|
|
55
|
+
lint: install ## Static analysis: black --check, pyflakes, bandit, vulture (runs before test/build)
|
|
56
|
+
@status=0; \
|
|
57
|
+
echo "-- black --check --"; \
|
|
58
|
+
$(UV) run black --check src tests vulture_whitelist.py || status=1; \
|
|
59
|
+
echo "-- pyflakes --"; \
|
|
60
|
+
$(UV) run pyflakes src tests || status=1; \
|
|
61
|
+
echo "-- bandit (src only -- tests use intentional fake credentials/asserts) --"; \
|
|
62
|
+
$(UV) run bandit -r -q src || status=1; \
|
|
63
|
+
echo "-- vulture (whitelisted for public API -- see vulture_whitelist.py) --"; \
|
|
64
|
+
$(UV) run vulture src vulture_whitelist.py || status=1; \
|
|
65
|
+
if [ $$status -eq 0 ]; then \
|
|
66
|
+
echo -e "$(GREEN)Lint clean.$(RESET)"; \
|
|
67
|
+
else \
|
|
68
|
+
echo -e "$(RED)Lint failed.$(RESET) See above. \`make format\` fixes black issues automatically."; \
|
|
69
|
+
exit 1; \
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
.PHONY: format
|
|
73
|
+
format: install ## Apply black formatting in place
|
|
74
|
+
@$(UV) run black src tests vulture_whitelist.py
|
|
75
|
+
|
|
76
|
+
.PHONY: test
|
|
77
|
+
test: lint ## Run the full test suite (integration tests auto-skip without `make compose-up`)
|
|
78
|
+
@$(UV) run pytest
|
|
79
|
+
|
|
80
|
+
.PHONY: test-unit
|
|
81
|
+
test-unit: lint ## Run only unit tests (tests/unit -- no external services)
|
|
82
|
+
@$(UV) run pytest tests/unit
|
|
83
|
+
|
|
84
|
+
.PHONY: test-integration
|
|
85
|
+
test-integration: lint compose-up ## Run integration tests (starts docker-compose emulators first)
|
|
86
|
+
@$(UV) run pytest tests/integration
|
|
87
|
+
|
|
88
|
+
.PHONY: check-docker
|
|
89
|
+
check-docker: ## Verify the Docker daemon is reachable
|
|
90
|
+
@if docker info >/dev/null 2>&1; then \
|
|
91
|
+
echo -e "$(GREEN)Docker daemon reachable.$(RESET)"; \
|
|
92
|
+
else \
|
|
93
|
+
echo -e "$(RED)Docker daemon not reachable.$(RESET) Start Docker (e.g. Docker Desktop) and retry."; \
|
|
94
|
+
exit 1; \
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
.PHONY: compose-up
|
|
98
|
+
compose-up: check-docker ## Start local S3/GCS emulators (moto-server, fake-gcs-server)
|
|
99
|
+
@docker compose up -d
|
|
100
|
+
@echo -e "$(GREEN)Emulators up.$(RESET) moto-server on :5001, fake-gcs-server on :4443."
|
|
101
|
+
|
|
102
|
+
.PHONY: compose-down
|
|
103
|
+
compose-down: check-docker ## Stop local S3/GCS emulators
|
|
104
|
+
@docker compose down
|
|
105
|
+
|
|
106
|
+
.PHONY: compose-logs
|
|
107
|
+
compose-logs: ## Tail logs from the emulator containers
|
|
108
|
+
@docker compose logs -f
|
|
109
|
+
|
|
110
|
+
.PHONY: build
|
|
111
|
+
build: lint ## Build the wheel/sdist into dist/
|
|
112
|
+
@$(UV) build
|
|
113
|
+
|
|
114
|
+
.PHONY: clean
|
|
115
|
+
clean: ## Remove the venv, caches, and build artifacts
|
|
116
|
+
@rm -rf .venv .pytest_cache dist build *.egg-info
|
|
117
|
+
@find . -name "__pycache__" -type d -prune -exec rm -rf {} +
|
|
118
|
+
@echo -e "$(GREEN)Cleaned.$(RESET)"
|