mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-31 20:29:46 +02:00
Compare commits
39
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e48c88c23 | ||
|
|
238f084efa | ||
|
|
284400e14f | ||
|
|
25b2b86d93 | ||
|
|
7e341e0f29 | ||
|
|
df577b9229 | ||
|
|
845e2d0a10 | ||
|
|
ee89bf5958 | ||
|
|
fc6298fccc | ||
|
|
67a0afc41a | ||
|
|
228a08b966 | ||
|
|
217795eb72 | ||
|
|
e873df678b | ||
|
|
2b603a6ab0 | ||
|
|
f60a06441b | ||
|
|
db5e956dc6 | ||
|
|
cacae7bd1f | ||
|
|
60df867872 | ||
|
|
9ed98d4798 | ||
|
|
7b36b4093d | ||
|
|
6fb2a93212 | ||
|
|
3f8944c1fc | ||
|
|
e79f3ceedc | ||
|
|
a64414c87c | ||
|
|
c9d85a22ec | ||
|
|
983243333c | ||
|
|
3a1c02ff33 | ||
|
|
5ab2aa79bb | ||
|
|
c33e64daa6 | ||
|
|
4ffae6065f | ||
|
|
54ddde9d4c | ||
|
|
60c41ce69e | ||
|
|
efd33d860f | ||
|
|
254a38560e | ||
|
|
3487f4eba5 | ||
|
|
3bdb7d09be | ||
|
|
3acf63a918 | ||
|
|
f51e5e2bd7 | ||
|
|
b20130d4e4 |
@@ -1,88 +0,0 @@
|
||||
# An action for setting up poetry install with caching.
|
||||
# Using a custom action since the default action does not
|
||||
# take poetry install groups into account.
|
||||
# Action code from:
|
||||
# https://github.com/actions/setup-python/issues/505#issuecomment-1273013236
|
||||
name: poetry-install-with-caching
|
||||
description: Poetry install with support for caching of dependency groups.
|
||||
|
||||
inputs:
|
||||
python-version:
|
||||
description: Python version, supporting MAJOR.MINOR only
|
||||
required: true
|
||||
|
||||
poetry-version:
|
||||
description: Poetry version
|
||||
required: true
|
||||
|
||||
cache-key:
|
||||
description: Cache key to use for manual handling of caching
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/setup-python@v5
|
||||
name: Setup python ${{ inputs.python-version }}
|
||||
id: setup-python
|
||||
with:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: cache-bin-poetry
|
||||
name: Cache Poetry binary - Python ${{ inputs.python-version }}
|
||||
env:
|
||||
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "1"
|
||||
with:
|
||||
path: |
|
||||
/opt/pipx/venvs/poetry
|
||||
# This step caches the poetry installation, so make sure it's keyed on the poetry version as well.
|
||||
key: bin-poetry-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-${{ inputs.poetry-version }}
|
||||
|
||||
- name: Refresh shell hashtable and fixup softlinks
|
||||
if: steps.cache-bin-poetry.outputs.cache-hit == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
POETRY_VERSION: ${{ inputs.poetry-version }}
|
||||
PYTHON_VERSION: ${{ inputs.python-version }}
|
||||
run: |
|
||||
set -eux
|
||||
|
||||
# Refresh the shell hashtable, to ensure correct `which` output.
|
||||
hash -r
|
||||
|
||||
# `actions/cache@v3` doesn't always seem able to correctly unpack softlinks.
|
||||
# Delete and recreate the softlinks pipx expects to have.
|
||||
rm /opt/pipx/venvs/poetry/bin/python
|
||||
cd /opt/pipx/venvs/poetry/bin
|
||||
ln -s "$(which "python$PYTHON_VERSION")" python
|
||||
chmod +x python
|
||||
cd /opt/pipx_bin/
|
||||
ln -s /opt/pipx/venvs/poetry/bin/poetry poetry
|
||||
chmod +x poetry
|
||||
|
||||
# Ensure everything got set up correctly.
|
||||
/opt/pipx/venvs/poetry/bin/python --version
|
||||
/opt/pipx_bin/poetry --version
|
||||
|
||||
- name: Install poetry
|
||||
if: steps.cache-bin-poetry.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
env:
|
||||
POETRY_VERSION: ${{ inputs.poetry-version }}
|
||||
PYTHON_VERSION: ${{ inputs.python-version }}
|
||||
# Install poetry using the python version installed by setup-python step.
|
||||
run: pipx install "poetry==$POETRY_VERSION" --python '${{ steps.setup-python.outputs.python-path }}' --verbose
|
||||
|
||||
- name: Restore pip and poetry cached dependencies
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "4"
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pip
|
||||
~/.cache/pypoetry/virtualenvs
|
||||
~/.cache/pypoetry/cache
|
||||
~/.cache/pypoetry/artifacts
|
||||
./.venv
|
||||
key: py-deps-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-poetry-${{ inputs.poetry-version }}-${{ inputs.cache-key }}-${{ hashFiles('./poetry.lock') }}
|
||||
@@ -3,9 +3,6 @@ name: CLI integration test
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -25,13 +22,11 @@ jobs:
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
with:
|
||||
filter: "libs/cli/**"
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
if: steps.changed-files.outputs.all
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: integration-test-cli
|
||||
- name: Setup env
|
||||
if: steps.changed-files.outputs.all
|
||||
working-directory: libs/cli/examples
|
||||
|
||||
@@ -9,8 +9,6 @@ on:
|
||||
description: "From which folder this pipeline executes"
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
# This env var allows us to get inline annotations when ruff has complaints.
|
||||
RUFF_OUTPUT_FORMAT: github
|
||||
|
||||
@@ -36,32 +34,18 @@ jobs:
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
with:
|
||||
filter: "${{ inputs.working-directory }}/**"
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
if: steps.changed-files.outputs.all
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: lint-${{ inputs.working-directory }}
|
||||
|
||||
- name: Check Poetry File
|
||||
if: steps.changed-files.outputs.all
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: poetry check
|
||||
enable-cache: true
|
||||
cache-suffix: lint-${{ inputs.working-directory }}
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.all
|
||||
# Also installs dev/lint/test/typing dependencies, to ensure we have
|
||||
# type hints for as many of our libraries as possible.
|
||||
# This helps catch errors that require dependencies to be spotted, for example:
|
||||
# https://github.com/langchain-ai/langchain/pull/10249/files#diff-935185cd488d015f026dcd9e19616ff62863e8cde8c0bee70318d3ccbca98341
|
||||
#
|
||||
# If you change this configuration, make sure to change the `cache-key`
|
||||
# in the `poetry_setup` action above to stop using the old cache.
|
||||
# It doesn't matter how you change it, any change will cause a cache-bust.
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: poetry install --with dev
|
||||
run: uv sync --frozen --group dev
|
||||
|
||||
- name: Get .mypy_cache to speed up mypy
|
||||
if: steps.changed-files.outputs.all
|
||||
@@ -71,7 +55,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
${{ inputs.working-directory }}/.mypy_cache
|
||||
key: mypy-lint-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/poetry.lock', inputs.working-directory)) }}
|
||||
key: mypy-lint-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/uv.lock', inputs.working-directory)) }}
|
||||
|
||||
- name: Analysing package code with our lint
|
||||
if: steps.changed-files.outputs.all
|
||||
@@ -86,17 +70,8 @@ jobs:
|
||||
|
||||
- name: Install test dependencies
|
||||
if: steps.changed-files.outputs.all
|
||||
# Also installs dev/lint/test/typing dependencies, to ensure we have
|
||||
# type hints for as many of our libraries as possible.
|
||||
# This helps catch errors that require dependencies to be spotted, for example:
|
||||
# https://github.com/langchain-ai/langchain/pull/10249/files#diff-935185cd488d015f026dcd9e19616ff62863e8cde8c0bee70318d3ccbca98341
|
||||
#
|
||||
# If you change this configuration, make sure to change the `cache-key`
|
||||
# in the `poetry_setup` action above to stop using the old cache.
|
||||
# It doesn't matter how you change it, any change will cause a cache-bust.
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
poetry install --with dev
|
||||
run: uv sync --group dev
|
||||
|
||||
- name: Get .mypy_cache_test to speed up mypy
|
||||
if: steps.changed-files.outputs.all
|
||||
@@ -106,7 +81,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
${{ inputs.working-directory }}/.mypy_cache_test
|
||||
key: mypy-test-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/poetry.lock', inputs.working-directory)) }}
|
||||
key: mypy-test-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/uv.lock', inputs.working-directory)) }}
|
||||
|
||||
- name: Analysing tests with our lint
|
||||
if: steps.changed-files.outputs.all
|
||||
|
||||
+15
-11
@@ -8,9 +8,6 @@ on:
|
||||
type: string
|
||||
description: "From which folder this pipeline executes"
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -26,12 +23,12 @@ jobs:
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: test-${{ inputs.working-directory }}
|
||||
enable-cache: true
|
||||
cache-siffix: test-${{ inputs.working-directory }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
@@ -42,14 +39,21 @@ jobs:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
poetry install --with dev
|
||||
run: uv sync --frozen --group dev
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
make test
|
||||
run: make test
|
||||
|
||||
- name: Install min version of deps
|
||||
shell: bash
|
||||
run: uv sync --frozen --all-extras --resolution lowest-direct --force-reinstall
|
||||
|
||||
- name: Run tests with min version of deps
|
||||
shell: bash
|
||||
run: make test
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Ensure the tests did not create any additional files
|
||||
shell: bash
|
||||
|
||||
@@ -3,9 +3,6 @@ name: test
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -24,12 +21,12 @@ jobs:
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: test-langgraph
|
||||
enable-cache: true
|
||||
cache-suffix: "test-langgraph"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
@@ -39,13 +36,20 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
poetry install --with dev
|
||||
run: uv sync --frozen --group dev
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
make test_parallel
|
||||
run: make test_parallel
|
||||
|
||||
- name: Install min version of deps
|
||||
shell: bash
|
||||
run: uv sync --frozen --all-extras --resolution lowest-direct --force-reinstall
|
||||
|
||||
- name: Run tests with min version of deps
|
||||
shell: bash
|
||||
run: make test
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Ensure the tests did not create any additional files
|
||||
shell: bash
|
||||
|
||||
@@ -9,7 +9,6 @@ on:
|
||||
description: "From which folder this pipeline executes"
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
PYTHON_VERSION: "3.10"
|
||||
|
||||
jobs:
|
||||
@@ -24,12 +23,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python $${ env.PYTHON_VERSION }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: release
|
||||
enable-cache: true
|
||||
cache-suffix: "release"
|
||||
|
||||
# We want to keep this build stage *separate* from the release stage,
|
||||
# so that there's no sharing of permissions between them.
|
||||
@@ -43,7 +42,7 @@ jobs:
|
||||
# > from the publish job.
|
||||
# https://github.com/pypa/gh-action-pypi-publish#non-goals
|
||||
- name: Build project for distribution
|
||||
run: poetry build
|
||||
run: uv build
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Upload build
|
||||
@@ -57,8 +56,8 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
echo pkg-name="$(poetry version | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT
|
||||
echo version="$(poetry version --short)" >> $GITHUB_OUTPUT
|
||||
echo pkg-name=$(grep -m 1 "^name = " pyproject.toml | cut -d '"' -f 2)
|
||||
echo version=$(grep -m 1 "^version = " pyproject.toml | cut -d '"' -f 2)
|
||||
|
||||
publish:
|
||||
needs:
|
||||
|
||||
@@ -3,9 +3,6 @@ name: test
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -21,12 +18,12 @@ jobs:
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: test-scheduler-kafka
|
||||
enable-cache: true
|
||||
cache-suffix: "test-scheduler-kafka"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
@@ -36,13 +33,20 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
poetry install --with dev
|
||||
run: uv sync --frozen --group dev
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
make test
|
||||
run: make test
|
||||
|
||||
- name: Install min version of deps
|
||||
shell: bash
|
||||
run: uv sync --frozen --all-extras --resolution lowest-direct --force-reinstall
|
||||
|
||||
- name: Run tests with min version of deps
|
||||
shell: bash
|
||||
run: make test
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Ensure the tests did not create any additional files
|
||||
shell: bash
|
||||
|
||||
@@ -7,9 +7,6 @@ on:
|
||||
paths:
|
||||
- "libs/**"
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -19,14 +16,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: SHA=$(git rev-parse HEAD) && echo "SHA=$SHA" >> $GITHUB_ENV
|
||||
- name: Set up Python 3.11 + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python 3.11
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: bench
|
||||
enable-cache: true
|
||||
cache-suffix: "bench"
|
||||
- name: Install dependencies
|
||||
run: poetry install --with dev
|
||||
run: uv sync --group dev
|
||||
- name: Run benchmarks
|
||||
run: OUTPUT=out/benchmark-baseline.json make -s benchmark
|
||||
- name: Save outputs
|
||||
|
||||
@@ -5,9 +5,6 @@ on:
|
||||
paths:
|
||||
- "libs/**"
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -21,14 +18,14 @@ jobs:
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
with:
|
||||
format: json
|
||||
- name: Set up Python 3.11 + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python 3.11
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: bench
|
||||
enable-cache: true
|
||||
cache-suffix: "bench"
|
||||
- name: Install dependencies
|
||||
run: poetry install --with dev
|
||||
run: uv sync --group dev
|
||||
- name: Download baseline
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
@@ -53,7 +50,7 @@ jobs:
|
||||
echo 'OUTPUT<<EOF'
|
||||
mv out/benchmark-baseline.json out/main.json
|
||||
mv out/benchmark.json out/changes.json
|
||||
poetry run pyperf compare_to out/main.json out/changes.json --table --group-by-speed
|
||||
uv run pyperf compare_to out/main.json out/changes.json --table --group-by-speed
|
||||
echo EOF
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
- name: Annotation
|
||||
|
||||
@@ -16,9 +16,6 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -125,26 +122,26 @@ jobs:
|
||||
- "3.11"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: schema-check-cli
|
||||
enable-cache: true
|
||||
cache-suffix: "schema-check-cli"
|
||||
- name: Install CLI dependencies
|
||||
run: |
|
||||
cd libs/cli
|
||||
poetry install
|
||||
uv sync
|
||||
- name: Generate schema and check for changes
|
||||
run: |
|
||||
cd libs/cli
|
||||
# Create a temporary copy of the current schema
|
||||
cp schemas/schema.json schemas/schema.current.json
|
||||
# Generate new schema
|
||||
poetry run python generate_schema.py
|
||||
uv run python generate_schema.py
|
||||
# Compare the new schema with the original
|
||||
if ! diff -q schemas/schema.json schemas/schema.current.json > /dev/null; then
|
||||
echo "Error: Langgraph.json configuration schema has changed. Please run 'poetry run python generate_schema.py' in the libs/cli directory and commit the changes."
|
||||
echo "Error: Langgraph.json configuration schema has changed. Please run 'uv run python generate_schema.py' in the libs/cli directory and commit the changes."
|
||||
diff schemas/schema.json schemas/schema.current.json
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -9,9 +9,6 @@ on:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
@@ -57,21 +54,21 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: "3.12"
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: docs
|
||||
enable-cache: true
|
||||
cache-suffix: "docs"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
yarn
|
||||
poetry install --with test --with docs --no-root
|
||||
uv sync --all-groups
|
||||
# we run this installation only for internal PRs
|
||||
# as GITHUB_TOKEN is not available for PRs from outside contributors
|
||||
if [ -n "${GITHUB_TOKEN}" ]; then
|
||||
poetry run pip install "git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git"
|
||||
uv run pip install "git+https://${GITHUB_TOKEN}@github.com/langchain-ai/mkdocs-material-insiders.git"
|
||||
fi
|
||||
|
||||
- name: Run unit tests
|
||||
@@ -103,7 +100,7 @@ jobs:
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "schedule" ]; then
|
||||
echo "Running link check on all HTML files matching notebooks in docs directory..."
|
||||
poetry run pytest -v \
|
||||
uv run pytest -v \
|
||||
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
|
||||
--check-links-ignore "https://academy\.langchain\.com/.*" \
|
||||
--check-links-ignore "https://x.com/.*" \
|
||||
@@ -128,7 +125,7 @@ jobs:
|
||||
echo "Changed files: ${CHANGED_FILES}"
|
||||
if [ -n "${CHANGED_FILES}" ]; then
|
||||
echo "Running link check on HTML files matching changed notebook files..."
|
||||
poetry run pytest -v \
|
||||
uv run pytest -v \
|
||||
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
|
||||
--check-links-ignore "https://academy\.langchain\.com/.*" \
|
||||
--check-links-ignore "http://localhost:8123/.*" \
|
||||
|
||||
@@ -11,9 +11,6 @@ on:
|
||||
- cron: "0 5 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -10,7 +10,6 @@ on:
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: "3.11"
|
||||
POETRY_VERSION: "2.1.2"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -26,12 +25,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: release
|
||||
enable-cache: true
|
||||
cache-suffix: "release"
|
||||
|
||||
# We want to keep this build stage *separate* from the release stage,
|
||||
# so that there's no sharing of permissions between them.
|
||||
@@ -45,7 +44,7 @@ jobs:
|
||||
# > from the publish job.
|
||||
# https://github.com/pypa/gh-action-pypi-publish#non-goals
|
||||
- name: Build project for distribution
|
||||
run: poetry build
|
||||
run: uv build
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Upload build
|
||||
@@ -59,8 +58,8 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
PKG_NAME="$(poetry version | cut -d ' ' -f 1)"
|
||||
VERSION="$(poetry version --short)"
|
||||
PKG_NAME=$(grep -m 1 "^name = " pyproject.toml | cut -d '"' -f 2)
|
||||
VERSION=$(grep -m 1 "^version = " pyproject.toml | cut -d '"' -f 2)
|
||||
SHORT_PKG_NAME="$(echo "$PKG_NAME" | sed -e 's/langgraph//g' -e 's/-//g')"
|
||||
if [ -z $SHORT_PKG_NAME ]; then
|
||||
TAG="$VERSION"
|
||||
@@ -163,11 +162,11 @@ jobs:
|
||||
# - The package is published, and it breaks on the missing dependency when
|
||||
# used in the real world.
|
||||
|
||||
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
enable-cache: true
|
||||
|
||||
- name: Import published package
|
||||
shell: bash
|
||||
@@ -185,18 +184,18 @@ jobs:
|
||||
# - attempt install again after 5 seconds if it fails because there is
|
||||
# sometimes a delay in availability on test pypi
|
||||
run: |
|
||||
poetry run pip install \
|
||||
uv run pip install \
|
||||
--extra-index-url https://test.pypi.org/simple/ \
|
||||
"$PKG_NAME==$VERSION" || \
|
||||
( \
|
||||
sleep 5 && \
|
||||
poetry run pip install \
|
||||
uv run pip install \
|
||||
--extra-index-url https://test.pypi.org/simple/ \
|
||||
"$PKG_NAME==$VERSION" \
|
||||
)
|
||||
|
||||
if [[ "$PKG_NAME" == *prebuilt* ]]; then
|
||||
poetry run pip install langgraph
|
||||
uv run pip install langgraph
|
||||
fi
|
||||
|
||||
if [[ "$PKG_NAME" == *checkpoint* || "$PKG_NAME" == *prebuilt* ]]; then
|
||||
@@ -209,10 +208,10 @@ jobs:
|
||||
IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/_/g)"
|
||||
fi
|
||||
|
||||
poetry run python -c "import $IMPORT_NAME; print(dir($IMPORT_NAME))"
|
||||
uv run python -c "import $IMPORT_NAME; print(dir($IMPORT_NAME))"
|
||||
|
||||
- name: Import test dependencies
|
||||
run: poetry install --with dev
|
||||
run: uv sync --group dev
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
# Overwrite the local version of the package with the test PyPI version.
|
||||
@@ -223,7 +222,7 @@ jobs:
|
||||
PKG_NAME: ${{ needs.build.outputs.pkg-name }}
|
||||
VERSION: ${{ needs.build.outputs.version }}
|
||||
run: |
|
||||
poetry run pip install \
|
||||
uv run pip install \
|
||||
--extra-index-url https://test.pypi.org/simple/ \
|
||||
"$PKG_NAME==$VERSION"
|
||||
|
||||
@@ -253,12 +252,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: release
|
||||
enable-cache: true
|
||||
cache-suffix: "release"
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -294,12 +293,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
poetry-version: ${{ env.POETRY_VERSION }}
|
||||
cache-key: release
|
||||
enable-cache: true
|
||||
cache-suffix: "release"
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -27,30 +27,30 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python + Poetry
|
||||
uses: "./.github/actions/poetry_setup"
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: 3.11
|
||||
poetry-version: 2.1.2
|
||||
cache-key: test-langgraph-notebooks
|
||||
python-version: "3.11"
|
||||
enable-cache: true
|
||||
cache-suffix: "test-langgraph-notebooks"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
poetry install --with test --no-root
|
||||
poetry run pip install jupyter
|
||||
uv sync --group test
|
||||
uv run pip install jupyter
|
||||
|
||||
- name: Start services
|
||||
run: make start-services
|
||||
|
||||
- name: Pre-download tiktoken files
|
||||
run: |
|
||||
poetry run python _scripts/download_tiktoken.py
|
||||
uv run python _scripts/download_tiktoken.py
|
||||
|
||||
- name: Prepare notebooks
|
||||
run: |
|
||||
if [ "${{ matrix.lib-version }}" = "development" ]; then
|
||||
poetry run python _scripts/prepare_notebooks_for_ci.py --comment-install-cells
|
||||
uv run python _scripts/prepare_notebooks_for_ci.py --comment-install-cells
|
||||
else
|
||||
poetry run python _scripts/prepare_notebooks_for_ci.py
|
||||
uv run python _scripts/prepare_notebooks_for_ci.py
|
||||
fi
|
||||
|
||||
- name: Run notebooks
|
||||
|
||||
@@ -227,6 +227,7 @@ see a preview of the documentation on the pull request page.
|
||||
|
||||
From the **monorepo root**, run the following command to install the dependencies:
|
||||
|
||||
<!-- TODO -->
|
||||
```bash
|
||||
poetry install --with docs --no-root
|
||||
```
|
||||
|
||||
+16
-18
@@ -12,32 +12,30 @@ build-prebuilt:
|
||||
# generates the final prebuilt page.
|
||||
@if [ "$(DOWNLOAD_STATS)" = "true" ]; then \
|
||||
set -x; \
|
||||
poetry run python -m _scripts.third_party_page.get_download_stats stats.yml; \
|
||||
uv run python -m _scripts.third_party_page.get_download_stats stats.yml; \
|
||||
set +x; \
|
||||
else \
|
||||
set -x; \
|
||||
poetry run python -m _scripts.third_party_page.get_download_stats --fake stats.yml; \
|
||||
uv run python -m _scripts.third_party_page.get_download_stats --fake stats.yml; \
|
||||
set +x; \
|
||||
fi
|
||||
poetry run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/agents/prebuilt.md --language python
|
||||
uv run python -m _scripts.third_party_page.create_third_party_page stats.yml docs/agents/prebuilt.md --language python
|
||||
|
||||
build-docs: build-typedoc build-prebuilt
|
||||
poetry run python -m mkdocs build --clean -f mkdocs.yml --strict
|
||||
uv run python -m mkdocs build --clean -f mkdocs.yml --strict
|
||||
|
||||
llms-text:
|
||||
poetry run python -m _scripts.generate_llms_text docs/llms-full.txt
|
||||
uv run python -m _scripts.generate_llms_text docs/llms-full.txt
|
||||
|
||||
install-vercel-deps:
|
||||
dnf install -y python3.11
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
poetry self update 1.8.5
|
||||
# don't use vercel's python - it wasn't compiled with sqlite support, and it fails when installing ipython's kernel
|
||||
poetry env use /usr/bin/python3.11
|
||||
poetry install --with docs --with test --no-root
|
||||
curl -sL "https://astral.sh/uv/install.sh" | bash -s
|
||||
export PATH="${HOME}/.cargo/bin:${PATH}"
|
||||
uv venv --python 3.11
|
||||
uv sync --all-groups
|
||||
|
||||
tests:
|
||||
# Run unit tests
|
||||
poetry run pytest tests/unit_tests
|
||||
uv run pytest tests/unit_tests
|
||||
|
||||
|
||||
vercel-build-docs: install-vercel-deps
|
||||
@@ -45,10 +43,10 @@ vercel-build-docs: install-vercel-deps
|
||||
|
||||
|
||||
serve-clean-docs: clean-docs
|
||||
poetry run python -m mkdocs serve -c -f mkdocs.yml --strict -w ../libs/langgraph
|
||||
uv run python -m mkdocs serve -c -f mkdocs.yml --strict -w ../libs/langgraph
|
||||
|
||||
serve-docs: build-typedoc
|
||||
poetry run python -m mkdocs serve -f mkdocs.yml -w ../libs/langgraph -w ../libs/checkpoint -w ../libs/sdk-py --dirty
|
||||
uv run python -m mkdocs serve -f mkdocs.yml -w ../libs/langgraph -w ../libs/checkpoint -w ../libs/sdk-py --dirty
|
||||
|
||||
clean-docs:
|
||||
find ./docs -name "*.ipynb" -type f -delete
|
||||
@@ -56,13 +54,13 @@ clean-docs:
|
||||
|
||||
## Run format against the project documentation.
|
||||
format-docs:
|
||||
poetry run ruff format docs
|
||||
poetry run ruff check --fix docs
|
||||
uv run ruff format docs
|
||||
uv run ruff check --fix docs
|
||||
|
||||
# Check the docs for linting violations
|
||||
lint-docs:
|
||||
poetry run ruff format --check docs
|
||||
poetry run ruff check docs
|
||||
uv run ruff format --check docs
|
||||
uv run ruff check docs
|
||||
|
||||
codespell:
|
||||
./codespell_notebooks.sh .
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
To setup requirements for building docs you can run:
|
||||
|
||||
```bash
|
||||
poetry install --with test
|
||||
uv sync --group test
|
||||
```
|
||||
|
||||
## Serving documentation locally
|
||||
|
||||
@@ -8,7 +8,7 @@ execute_notebook() {
|
||||
file="$1"
|
||||
echo "Starting execution of $file"
|
||||
start_time=$(date +%s)
|
||||
if ! output=$(time poetry run jupyter execute "$file" 2>&1); then
|
||||
if ! output=$(time uv run jupyter execute "$file" 2>&1); then
|
||||
end_time=$(date +%s)
|
||||
execution_time=$((end_time - start_time))
|
||||
echo "Error in $file. Execution time: $execution_time seconds"
|
||||
|
||||
+19
-17
@@ -29,7 +29,7 @@ from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
# highlight-next-line
|
||||
async with MultiServerMCPClient(
|
||||
client = MultiServerMCPClient(
|
||||
{
|
||||
"math": {
|
||||
"command": "python",
|
||||
@@ -39,22 +39,24 @@ async with MultiServerMCPClient(
|
||||
},
|
||||
"weather": {
|
||||
# Ensure your start your weather server on port 8000
|
||||
"url": "http://localhost:8000/sse",
|
||||
"transport": "sse",
|
||||
"url": "http://localhost:8000/mcp",
|
||||
"transport": "streamable_http",
|
||||
}
|
||||
}
|
||||
) as client:
|
||||
agent = create_react_agent(
|
||||
"anthropic:claude-3-7-sonnet-latest",
|
||||
# highlight-next-line
|
||||
client.get_tools()
|
||||
)
|
||||
math_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
|
||||
)
|
||||
weather_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
|
||||
)
|
||||
)
|
||||
# highlight-next-line
|
||||
tools = await client.get_tools()
|
||||
agent = create_react_agent(
|
||||
"anthropic:claude-3-7-sonnet-latest",
|
||||
# highlight-next-line
|
||||
tools
|
||||
)
|
||||
math_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
|
||||
)
|
||||
weather_response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
|
||||
)
|
||||
```
|
||||
|
||||
## Custom MCP servers
|
||||
@@ -87,7 +89,7 @@ if __name__ == "__main__":
|
||||
mcp.run(transport="stdio")
|
||||
```
|
||||
|
||||
```python title="Example Weather Server (SSE transport)"
|
||||
```python title="Example Weather Server (Streamable HTTP transport)"
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Weather")
|
||||
@@ -98,7 +100,7 @@ async def get_weather(location: str) -> str:
|
||||
return "It's always sunny in New York"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="sse")
|
||||
mcp.run(transport="streamable-http")
|
||||
```
|
||||
|
||||
## Additional resources
|
||||
|
||||
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
@@ -56,22 +56,27 @@ cloudpickle>=3.0.0
|
||||
Example `pyproject.toml` file:
|
||||
|
||||
```toml
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "my-agent"
|
||||
version = "0.0.1"
|
||||
description = "An excellent agent build for LangGraph Platform."
|
||||
authors = ["Polly the parrot <1223+polly@users.noreply.github.com>"]
|
||||
license = "MIT"
|
||||
authors = [
|
||||
{name = "Polly the parrot", email = "1223+polly@users.noreply.github.com"}
|
||||
]
|
||||
license = {text = "MIT"}
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"langgraph>=0.2.0",
|
||||
"langchain-fireworks>=0.1.3"
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
langgraph = "^0.2.0"
|
||||
langchain-fireworks = "^0.1.3"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["my_agent"]
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
@@ -20,7 +20,7 @@ To deploy an application to **LangGraph Platform**, your application code must r
|
||||
## 2. Deploy to LangGraph Platform
|
||||
|
||||
1. Log in to [LangSmith](https://smith.langchain.com/).
|
||||
1. In the left sidebar, select **LangGraph Platform**.
|
||||
1. In the left sidebar, select **Deployments**.
|
||||
1. Click the **+ New Deployment** button. A pane will open where you can fill in the required fields.
|
||||
1. If you are a first time user or adding a private repository that has not been previously connected, click the **Import from GitHub** button and follow the instructions to connect your GitHub account.
|
||||
1. Select your New LangGraph Project repository.
|
||||
|
||||
@@ -7,7 +7,7 @@ search:
|
||||
|
||||
**LangGraph Server** offers an API for creating and managing agent-based applications. It is built on the concept of [assistants](assistants.md), which are agents configured for specific tasks, and includes built-in [persistence](persistence.md#memory-store) and a **task queue**. This versatile API supports a wide range of agentic application use cases, from background processing to real-time interactions.
|
||||
|
||||
Use LangGraph Serverto create and manage [assistants](assistants.md), [threads](../cloud/concepts/threads.md), [runs](../cloud/concepts/runs.md), [cron jobs](../cloud/concepts/cron_jobs.md), [webhooks](../cloud/concepts/webhooks.md), and more.
|
||||
Use LangGraph Server to create and manage [assistants](assistants.md), [threads](../cloud/concepts/threads.md), [runs](../cloud/concepts/runs.md), [cron jobs](../cloud/concepts/cron_jobs.md), [webhooks](../cloud/concepts/webhooks.md), and more.
|
||||
|
||||
!!! tip "API reference"
|
||||
|
||||
|
||||
@@ -583,7 +583,7 @@
|
||||
"def check_query(state: MessagesState):\n",
|
||||
" system_message = {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": generate_query_system_prompt,\n",
|
||||
" \"content\": check_query_system_prompt,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" # Generate an artificial user message to check\n",
|
||||
|
||||
@@ -122,8 +122,8 @@ As noted in the Anthropic blog on `Building Effective Agents`:
|
||||
|
||||
# Simple check - does the joke contain "?" or "!"
|
||||
if "?" in state["joke"] or "!" in state["joke"]:
|
||||
return "Fail"
|
||||
return "Pass"
|
||||
return "Pass"
|
||||
return "Fail"
|
||||
|
||||
|
||||
def improve_joke(state: State):
|
||||
|
||||
Generated
-9178
File diff suppressed because it is too large
Load Diff
+86
-75
@@ -1,88 +1,99 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-docs"
|
||||
version = "0.0.1"
|
||||
description = "LangGraph docs"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = "~=3.10"
|
||||
readme = "README.md"
|
||||
package-mode = false
|
||||
license = "MIT"
|
||||
dependencies = [
|
||||
"aiohappyeyeballs==2.4.3",
|
||||
"hub>=3.0.1,<4",
|
||||
"xxhash>=3.5.0,<4",
|
||||
"black>=25.1.0,<26",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
aiohappyeyeballs = "2.4.3"
|
||||
hub = "^3.0.1"
|
||||
xxhash = "^3.5.0"
|
||||
black = "^25.1.0"
|
||||
[dependency-groups]
|
||||
docs = [
|
||||
"langgraph",
|
||||
"langgraph-prebuilt",
|
||||
"langgraph-checkpoint",
|
||||
"langgraph-checkpoint-sqlite",
|
||||
"langgraph-checkpoint-postgres",
|
||||
"langgraph-sdk",
|
||||
"langgraph-supervisor",
|
||||
"langgraph-swarm",
|
||||
"langchain-mcp-adapters",
|
||||
"langchain-ollama",
|
||||
"mkdocs",
|
||||
"mkdocs-autorefs",
|
||||
"mkdocstrings",
|
||||
"mkdocstrings-python",
|
||||
"mkdocs-minify-plugin",
|
||||
"mkdocs-rss-plugin",
|
||||
"mkdocs-git-committers-plugin-2",
|
||||
"mkdocs-material[imaging]",
|
||||
"markdown-callouts",
|
||||
"markdown-include",
|
||||
"mkdocs-exclude",
|
||||
"psycopg[binary]",
|
||||
"psycopg-pool",
|
||||
"pygments-ansi-color",
|
||||
"vcrpy",
|
||||
"click",
|
||||
"ruff",
|
||||
"jupyter",
|
||||
"langchain-cohere",
|
||||
]
|
||||
test = [
|
||||
"langchain",
|
||||
"langchain-core",
|
||||
"langchain-openai",
|
||||
"langchain-anthropic",
|
||||
"langchain-nomic",
|
||||
"langchain-fireworks",
|
||||
"langchain-community",
|
||||
"langchain-tavily",
|
||||
"langchain-experimental",
|
||||
"langchain-mistralai",
|
||||
"langgraph-checkpoint-mongodb",
|
||||
"langmem",
|
||||
"langsmith",
|
||||
"chromadb",
|
||||
"gpt4all",
|
||||
"scikit-learn",
|
||||
"numexpr",
|
||||
"numpy",
|
||||
"matplotlib",
|
||||
"redis",
|
||||
"pymongo",
|
||||
"motor",
|
||||
"grandalf",
|
||||
"pyppeteer",
|
||||
"networkx",
|
||||
"autogen ; python_version >= '3.8' and python_version < '3.13'",
|
||||
"pytest",
|
||||
"pytest-check-links",
|
||||
]
|
||||
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
langgraph = { path = "../libs/langgraph/", develop = true }
|
||||
langgraph-prebuilt = {path = "../libs/prebuilt", develop = true}
|
||||
langgraph-checkpoint = { path = "../libs/checkpoint/", develop = true }
|
||||
langgraph-checkpoint-sqlite = { path = "../libs/checkpoint-sqlite", develop = true }
|
||||
langgraph-checkpoint-postgres = { path = "../libs/checkpoint-postgres", develop = true }
|
||||
langgraph-sdk = {path = "../libs/sdk-py", develop = true}
|
||||
# TODO: switch these to published versions
|
||||
[tool.uv]
|
||||
package = false
|
||||
default-groups = ["docs","test",]
|
||||
|
||||
[tool.uv.sources]
|
||||
langgraph = { path = "../libs/langgraph/", editable = true }
|
||||
langgraph-prebuilt = { path = "../libs/prebuilt", editable = true }
|
||||
langgraph-checkpoint = { path = "../libs/checkpoint/", editable = true }
|
||||
langgraph-checkpoint-sqlite = { path = "../libs/checkpoint-sqlite", editable = true }
|
||||
langgraph-checkpoint-postgres = { path = "../libs/checkpoint-postgres", editable = true }
|
||||
langgraph-sdk = { path = "../libs/sdk-py", editable = true }
|
||||
langgraph-supervisor = { git = "https://github.com/langchain-ai/langgraph-supervisor-py" }
|
||||
langgraph-swarm = { git = "https://github.com/langchain-ai/langgraph-swarm-py" }
|
||||
langchain-mcp-adapters = { git = "https://github.com/langchain-ai/langchain-mcp-adapters" }
|
||||
langchain-ollama = "^0.2.3"
|
||||
mkdocs = "*"
|
||||
mkdocs-autorefs = "*"
|
||||
mkdocstrings = "*"
|
||||
mkdocstrings-python = "*"
|
||||
mkdocs-minify-plugin = "*"
|
||||
mkdocs-rss-plugin = "*"
|
||||
mkdocs-git-committers-plugin-2 = "*"
|
||||
mkdocs-material = {extras = ["imaging"], version = "*"}
|
||||
markdown-callouts = "*"
|
||||
markdown-include = "*"
|
||||
mkdocs-exclude = "*"
|
||||
psycopg = {extras = ["binary"], version = "^3.2.0"}
|
||||
psycopg-pool = "^3.2.0"
|
||||
pygments-ansi-color = ">=0.3"
|
||||
vcrpy = "^6.0.1"
|
||||
click = "^8.1.7"
|
||||
ruff = "^0.6.8"
|
||||
jupyter = "^1.1.1"
|
||||
langchain-cohere = "^0.4.2"
|
||||
|
||||
[tool.poetry.group.test.dependencies]
|
||||
langchain = "^0.3.8"
|
||||
langchain-core = "^0.3.54"
|
||||
langchain-openai = "^0.3.7"
|
||||
langchain-anthropic = "^0.3.8"
|
||||
langchain-nomic = "^0.1.3"
|
||||
langchain-fireworks = "^0.2.0"
|
||||
langchain-community = "^0.3.0"
|
||||
langchain-tavily = "^0.1.5"
|
||||
langchain-experimental = "^0.3.2"
|
||||
langchain-mistralai = "^0.2.6"
|
||||
langgraph-checkpoint-mongodb = "^0.1.0"
|
||||
langmem = "^0.0.19"
|
||||
langsmith = "^0.3.0"
|
||||
chromadb = "^0.5.5"
|
||||
gpt4all = "^2.8.2"
|
||||
scikit-learn = "^1.5.2"
|
||||
numexpr = "^2.10.1"
|
||||
numpy = "^1.26.4"
|
||||
matplotlib = "^3.9.2"
|
||||
redis = "^5.0.8"
|
||||
pymongo = "^4.8.0"
|
||||
motor = "^3.5.1"
|
||||
grandalf = "^0.8"
|
||||
pyppeteer = "^2.0.0"
|
||||
networkx = "^3.3"
|
||||
autogen = { version = "^0.3.0", python = "<3.13,>=3.8" }
|
||||
pytest = "^8.3.5"
|
||||
pytest-check-links = "^0.10.1"
|
||||
|
||||
[tool.poetry.group.test]
|
||||
optional = true
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
||||
[tool.ruff]
|
||||
extend-include = ["*.ipynb"]
|
||||
|
||||
Generated
+6769
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ POSTGRES_VERSIONS ?= 15 16
|
||||
test_pg_version:
|
||||
@echo "Testing PostgreSQL $(POSTGRES_VERSION)"
|
||||
@POSTGRES_VERSION=$(POSTGRES_VERSION) make start-postgres
|
||||
@poetry run pytest $(TEST)
|
||||
@uv run pytest $(TEST)
|
||||
@EXIT_CODE=$$?; \
|
||||
make stop-postgres; \
|
||||
echo "Finished testing PostgreSQL $(POSTGRES_VERSION); Exit code: $$EXIT_CODE"; \
|
||||
@@ -36,7 +36,7 @@ test:
|
||||
TEST ?= .
|
||||
test_watch:
|
||||
POSTGRES_VERSION=${POSTGRES_VERSION:-16} make start-postgres; \
|
||||
poetry run ptw $(TEST); \
|
||||
uv run ptw $(TEST); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-postgres; \
|
||||
exit $$EXIT_CODE
|
||||
@@ -55,12 +55,12 @@ lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
uv run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
uv run ruff format $(PYTHON_FILES)
|
||||
uv run ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
@@ -333,7 +333,7 @@ class BasePostgresStore(Generic[C]):
|
||||
# First handle main store insertions
|
||||
for op in inserts:
|
||||
if op.ttl is not None:
|
||||
expires_at_str = f"NOW() + INTERVAL '{op.ttl*60} seconds'"
|
||||
expires_at_str = f"NOW() + INTERVAL '{op.ttl * 60} seconds'"
|
||||
ttl_minutes = op.ttl
|
||||
else:
|
||||
expires_at_str = "NULL"
|
||||
|
||||
Generated
-1443
File diff suppressed because it is too large
Load Diff
@@ -1,47 +1,53 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-checkpoint-postgres"
|
||||
version = "2.0.21"
|
||||
description = "Library with a Postgres implementation of LangGraph checkpoint saver."
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
packages = [{ include = "langgraph" }]
|
||||
license = "MIT"
|
||||
license-files = ['LICENSE']
|
||||
dependencies = [
|
||||
"langgraph-checkpoint>=2.0.21",
|
||||
"orjson>=3.10.1",
|
||||
"psycopg>=3.2.0",
|
||||
"psycopg-pool>=3.2.0",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
langgraph-checkpoint = "^2.0.21"
|
||||
orjson = ">=3.10.1"
|
||||
psycopg = "^3.2.0"
|
||||
psycopg-pool = "^3.2.0"
|
||||
[project.urls]
|
||||
Repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
codespell = "^2.2.0"
|
||||
pytest = "^7.2.1"
|
||||
anyio = "^4.4.0"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
mypy = "^1.10.0"
|
||||
psycopg = {extras = ["binary"], version = ">=3.0.0"}
|
||||
langgraph-checkpoint = {path = "../checkpoint", develop = true}
|
||||
pytest-watcher = { version = ">=0.4.3", python = "<4.0" }
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff",
|
||||
"codespell",
|
||||
"pytest",
|
||||
"anyio",
|
||||
"pytest-asyncio",
|
||||
"pytest-mock",
|
||||
"mypy",
|
||||
"psycopg[binary]",
|
||||
"langgraph-checkpoint",
|
||||
"pytest-watcher",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
default-groups = ['dev']
|
||||
|
||||
[tool.uv.sources]
|
||||
langgraph-checkpoint = { path = "../checkpoint", editable = true }
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
include = ["langgraph"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# --strict-markers will raise errors on unknown marks.
|
||||
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
|
||||
#
|
||||
# https://docs.pytest.org/en/7.1.x/reference/reference.html
|
||||
# --strict-config any warnings encountered while parsing the `pytest`
|
||||
# section of the configuration file raise errors.
|
||||
addopts = "--strict-markers --strict-config --durations=5 -vv"
|
||||
asyncio_mode = "auto"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
|
||||
Generated
+1206
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,10 @@
|
||||
######################
|
||||
|
||||
test:
|
||||
poetry run pytest tests
|
||||
uv run pytest tests
|
||||
|
||||
test_watch:
|
||||
poetry run ptw .
|
||||
uv run ptw .
|
||||
|
||||
######################
|
||||
# LINTING AND FORMATTING
|
||||
@@ -24,12 +24,12 @@ lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
uv run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
uv run ruff format $(PYTHON_FILES)
|
||||
uv run ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import random
|
||||
import sqlite3
|
||||
import threading
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from contextlib import closing, contextmanager
|
||||
from typing import Any, AsyncIterator, Dict, Iterator, Optional, Sequence, Tuple
|
||||
from typing import Any, Optional, cast
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
@@ -261,7 +262,12 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
||||
return CheckpointTuple(
|
||||
config,
|
||||
self.serde.loads_typed((type, checkpoint)),
|
||||
self.jsonplus_serde.loads(metadata) if metadata is not None else {},
|
||||
cast(
|
||||
CheckpointMetadata,
|
||||
self.jsonplus_serde.loads(metadata)
|
||||
if metadata is not None
|
||||
else {},
|
||||
),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
@@ -283,7 +289,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
||||
self,
|
||||
config: Optional[RunnableConfig],
|
||||
*,
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
filter: Optional[dict[str, Any]] = None,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
limit: Optional[int] = None,
|
||||
) -> Iterator[CheckpointTuple]:
|
||||
@@ -349,7 +355,12 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
||||
}
|
||||
},
|
||||
self.serde.loads_typed((type, checkpoint)),
|
||||
self.jsonplus_serde.loads(metadata) if metadata is not None else {},
|
||||
cast(
|
||||
CheckpointMetadata,
|
||||
self.jsonplus_serde.loads(metadata)
|
||||
if metadata is not None
|
||||
else {},
|
||||
),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
@@ -428,7 +439,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
||||
def put_writes(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
writes: Sequence[Tuple[str, Any]],
|
||||
writes: Sequence[tuple[str, Any]],
|
||||
task_id: str,
|
||||
task_path: str = "",
|
||||
) -> None:
|
||||
@@ -496,7 +507,7 @@ class SqliteSaver(BaseCheckpointSaver[str]):
|
||||
self,
|
||||
config: Optional[RunnableConfig],
|
||||
*,
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
filter: Optional[dict[str, Any]] = None,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
limit: Optional[int] = None,
|
||||
) -> AsyncIterator[CheckpointTuple]:
|
||||
|
||||
@@ -2,7 +2,7 @@ import asyncio
|
||||
import random
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, Callable, Optional, TypeVar
|
||||
from typing import Any, Callable, Optional, TypeVar, cast
|
||||
|
||||
import aiosqlite
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
@@ -374,7 +374,12 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
|
||||
return CheckpointTuple(
|
||||
config,
|
||||
self.serde.loads_typed((type, checkpoint)),
|
||||
self.jsonplus_serde.loads(metadata) if metadata is not None else {},
|
||||
cast(
|
||||
CheckpointMetadata,
|
||||
self.jsonplus_serde.loads(metadata)
|
||||
if metadata is not None
|
||||
else {},
|
||||
),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
@@ -449,7 +454,12 @@ class AsyncSqliteSaver(BaseCheckpointSaver[str]):
|
||||
}
|
||||
},
|
||||
self.serde.loads_typed((type, checkpoint)),
|
||||
self.jsonplus_serde.loads(metadata) if metadata is not None else {},
|
||||
cast(
|
||||
CheckpointMetadata,
|
||||
self.jsonplus_serde.loads(metadata)
|
||||
if metadata is not None
|
||||
else {},
|
||||
),
|
||||
(
|
||||
{
|
||||
"configurable": {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
from typing import Any, Dict, Optional, Sequence, Tuple
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
@@ -7,8 +8,8 @@ from langgraph.checkpoint.base import get_checkpoint_id
|
||||
|
||||
|
||||
def _metadata_predicate(
|
||||
metadata_filter: Dict[str, Any],
|
||||
) -> Tuple[Sequence[str], Sequence[Any]]:
|
||||
metadata_filter: dict[str, Any],
|
||||
) -> tuple[Sequence[str], Sequence[Any]]:
|
||||
"""Return WHERE clause predicates for (a)search() given metadata filter.
|
||||
|
||||
This method returns a tuple of a string and a tuple of values. The string
|
||||
@@ -17,7 +18,7 @@ def _metadata_predicate(
|
||||
for each of the corresponding parameters.
|
||||
"""
|
||||
|
||||
def _where_value(query_value: Any) -> Tuple[str, Any]:
|
||||
def _where_value(query_value: Any) -> tuple[str, Any]:
|
||||
"""Return tuple of operator and value for WHERE clause predicate."""
|
||||
if query_value is None:
|
||||
return ("IS ?", None)
|
||||
@@ -52,9 +53,9 @@ def _metadata_predicate(
|
||||
|
||||
def search_where(
|
||||
config: Optional[RunnableConfig],
|
||||
filter: Optional[Dict[str, Any]],
|
||||
filter: Optional[dict[str, Any]],
|
||||
before: Optional[RunnableConfig] = None,
|
||||
) -> Tuple[str, Sequence[Any]]:
|
||||
) -> tuple[str, Sequence[Any]]:
|
||||
"""Return WHERE clause predicates for (a)search() given metadata filter
|
||||
and `before` config.
|
||||
|
||||
|
||||
Generated
-1047
File diff suppressed because it is too large
Load Diff
@@ -1,43 +1,49 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-checkpoint-sqlite"
|
||||
version = "2.0.7"
|
||||
description = "Library with a SQLite implementation of LangGraph checkpoint saver."
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
packages = [{ include = "langgraph" }]
|
||||
license = "MIT"
|
||||
license-files = ['LICENSE']
|
||||
dependencies = [
|
||||
"langgraph-checkpoint>=2.0.15",
|
||||
"aiosqlite>=0.20",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
langgraph-checkpoint = "^2.0.15"
|
||||
aiosqlite = ">=0.20,<0.22"
|
||||
[project.urls]
|
||||
Repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
codespell = "^2.2.0"
|
||||
pytest = "^7.2.1"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-watcher = { version = ">=0.4.1", python = "<4.0" }
|
||||
mypy = "^1.10.0"
|
||||
langgraph-checkpoint = {path = "../checkpoint", develop = true}
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff",
|
||||
"codespell",
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-mock",
|
||||
"pytest-watcher",
|
||||
"mypy",
|
||||
"langgraph-checkpoint",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
default-groups = ['dev']
|
||||
|
||||
[tool.uv.sources]
|
||||
langgraph-checkpoint = { path = "../checkpoint", editable = true }
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
include = ["langgraph"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# --strict-markers will raise errors on unknown marks.
|
||||
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
|
||||
#
|
||||
# https://docs.pytest.org/en/7.1.x/reference/reference.html
|
||||
# --strict-config any warnings encountered while parsing the `pytest`
|
||||
# section of the configuration file raise errors.
|
||||
addopts = "--strict-markers --strict-config --durations=5 -vv"
|
||||
asyncio_mode = "auto"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
|
||||
@@ -59,7 +59,7 @@ class TestAsyncSqliteSaver:
|
||||
|
||||
async def test_combined_metadata(self) -> None:
|
||||
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
|
||||
config = {
|
||||
config: RunnableConfig = {
|
||||
"configurable": {
|
||||
"thread_id": "thread-2",
|
||||
"checkpoint_ns": "",
|
||||
@@ -69,7 +69,7 @@ class TestAsyncSqliteSaver:
|
||||
}
|
||||
await saver.aput(config, self.chkpnt_2, self.metadata_2, {})
|
||||
checkpoint = await saver.aget_tuple(config)
|
||||
assert checkpoint.metadata == {
|
||||
assert checkpoint is not None and checkpoint.metadata == {
|
||||
**self.metadata_2,
|
||||
"thread_id": "thread-2",
|
||||
"run_id": "my_run_id",
|
||||
|
||||
@@ -60,7 +60,7 @@ class TestSqliteSaver:
|
||||
|
||||
def test_combined_metadata(self) -> None:
|
||||
with SqliteSaver.from_conn_string(":memory:") as saver:
|
||||
config = {
|
||||
config: RunnableConfig = {
|
||||
"configurable": {
|
||||
"thread_id": "thread-2",
|
||||
"checkpoint_ns": "",
|
||||
@@ -70,7 +70,7 @@ class TestSqliteSaver:
|
||||
}
|
||||
saver.put(config, self.chkpnt_2, self.metadata_2, {})
|
||||
checkpoint = saver.get_tuple(config)
|
||||
assert checkpoint.metadata == {
|
||||
assert checkpoint is not None and checkpoint.metadata == {
|
||||
**self.metadata_2,
|
||||
"thread_id": "thread-2",
|
||||
"run_id": "my_run_id",
|
||||
|
||||
Generated
+1109
File diff suppressed because it is too large
Load Diff
@@ -7,10 +7,10 @@
|
||||
TEST ?= .
|
||||
|
||||
test:
|
||||
poetry run pytest $(TEST)
|
||||
uv run pytest $(TEST)
|
||||
|
||||
test_watch:
|
||||
poetry run ptw $(TEST)
|
||||
uv run ptw $(TEST)
|
||||
|
||||
######################
|
||||
# LINTING AND FORMATTING
|
||||
@@ -26,12 +26,12 @@ lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
uv run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
uv run ruff format $(PYTHON_FILES)
|
||||
uv run ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Mapping
|
||||
from typing import Generic, Sequence, TypeVar
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from langgraph.checkpoint.serde.base import SerializerProtocol
|
||||
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
|
||||
|
||||
@@ -6,7 +6,7 @@ Bundled in to avoid install issues with uuid6 package
|
||||
import random
|
||||
import time
|
||||
import uuid
|
||||
from typing import Optional, Tuple
|
||||
from typing import Optional
|
||||
|
||||
_last_v6_timestamp = None
|
||||
|
||||
@@ -21,7 +21,7 @@ class UUID(uuid.UUID):
|
||||
hex: Optional[str] = None,
|
||||
bytes: Optional[bytes] = None,
|
||||
bytes_le: Optional[bytes] = None,
|
||||
fields: Optional[Tuple[int, int, int, int, int, int]] = None,
|
||||
fields: Optional[tuple[int, int, int, int, int, int]] = None,
|
||||
int: Optional[int] = None,
|
||||
version: Optional[int] = None,
|
||||
*,
|
||||
|
||||
@@ -20,11 +20,11 @@ from ipaddress import (
|
||||
)
|
||||
from typing import Any, Callable, Optional, Union, cast
|
||||
from uuid import UUID
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import ormsgpack
|
||||
from langchain_core.load.load import Reviver
|
||||
from langchain_core.load.serializable import Serializable
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from langgraph.checkpoint.serde.base import SerializerProtocol
|
||||
from langgraph.checkpoint.serde.types import SendProtocol
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from collections.abc import Sequence
|
||||
from typing import (
|
||||
Any,
|
||||
Optional,
|
||||
Protocol,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
runtime_checkable,
|
||||
)
|
||||
|
||||
@@ -10,10 +10,10 @@ Core types:
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Iterable
|
||||
from datetime import datetime
|
||||
from typing import (
|
||||
Any,
|
||||
Iterable,
|
||||
Literal,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
|
||||
@@ -9,7 +9,8 @@ asynchronous operations.
|
||||
import asyncio
|
||||
import functools
|
||||
import json
|
||||
from typing import Any, Awaitable, Callable, Optional, Sequence, Union
|
||||
from collections.abc import Awaitable, Sequence
|
||||
from typing import Any, Callable, Optional, Union
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
@@ -104,9 +104,10 @@ import concurrent.futures as cf
|
||||
import functools
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from collections.abc import Iterable
|
||||
from datetime import datetime, timezone
|
||||
from importlib import util
|
||||
from typing import Any, Iterable, Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
Generated
-1069
File diff suppressed because it is too large
Load Diff
@@ -1,43 +1,43 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-checkpoint"
|
||||
version = "2.0.25"
|
||||
version = "2.0.26"
|
||||
description = "Library with base interfaces for LangGraph checkpoint savers."
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
packages = [{ include = "langgraph" }]
|
||||
license = "MIT"
|
||||
license-files = ['LICENSE']
|
||||
dependencies = [
|
||||
"langchain-core>=0.2.38",
|
||||
"ormsgpack>=1.8.0",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
langchain-core = { version = ">=0.2.38", python = "<4.0" }
|
||||
ormsgpack = "^1.8.0"
|
||||
[project.urls]
|
||||
Repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
codespell = "^2.2.0"
|
||||
pytest = "^7.2.1"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-watcher = { version = ">=0.4.1", python = "<4.0" }
|
||||
mypy = "^1.10.0"
|
||||
dataclasses-json = { version = ">=0.6.7", python = "<4.0" }
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff",
|
||||
"codespell",
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-mock",
|
||||
"pytest-watcher",
|
||||
"mypy",
|
||||
"dataclasses-json",
|
||||
]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
include = ["langgraph"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# --strict-markers will raise errors on unknown marks.
|
||||
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
|
||||
#
|
||||
# https://docs.pytest.org/en/7.1.x/reference/reference.html
|
||||
# --strict-config any warnings encountered while parsing the `pytest`
|
||||
# section of the configuration file raise errors.
|
||||
addopts = "--strict-markers --strict-config --durations=5 -vv"
|
||||
asyncio_mode = "auto"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
|
||||
@@ -8,12 +8,12 @@ from datetime import date, datetime, time, timezone
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from ipaddress import IPv4Address
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import dataclasses_json
|
||||
from pydantic import BaseModel, SecretStr
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
from pydantic.v1 import SecretStr as SecretStrV1
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from langgraph.checkpoint.serde.jsonplus import (
|
||||
JsonPlusSerializer,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# mypy: disable-error-code="operator"
|
||||
import asyncio
|
||||
import json
|
||||
from collections.abc import Iterable
|
||||
from datetime import datetime
|
||||
from typing import Any, Iterable
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
Generated
+1108
File diff suppressed because it is too large
Load Diff
+9
-9
@@ -5,9 +5,9 @@
|
||||
######################
|
||||
|
||||
test:
|
||||
poetry run pytest tests/unit_tests
|
||||
uv run pytest tests/unit_tests
|
||||
test-integration:
|
||||
poetry run pytest tests/integration_tests
|
||||
uv run pytest tests/integration_tests
|
||||
|
||||
######################
|
||||
# LINTING AND FORMATTING
|
||||
@@ -23,14 +23,14 @@ lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) || poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
uv run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) || uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
uv run ruff format $(PYTHON_FILES)
|
||||
uv run ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
update-schema:
|
||||
poetry run python generate_schema.py
|
||||
uv run python generate_schema.py
|
||||
|
||||
+4
-4
@@ -87,17 +87,17 @@ To develop the CLI itself:
|
||||
|
||||
1. Clone the repository
|
||||
2. Navigate to the CLI directory: `cd libs/cli`
|
||||
3. Install development dependencies: `poetry install`
|
||||
3. Install development dependencies: `uv pip install`
|
||||
4. Make your changes to the CLI code
|
||||
5. Test your changes:
|
||||
```bash
|
||||
# Run CLI commands directly
|
||||
poetry run langgraph --help
|
||||
uv run langgraph --help
|
||||
|
||||
# Or use the examples
|
||||
cd examples
|
||||
poetry install
|
||||
poetry run langgraph dev # or other commands
|
||||
uv pip install
|
||||
uv run langgraph dev # or other commands
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
.PHONY: run_w_override
|
||||
|
||||
run:
|
||||
poetry run langgraph up --watch --no-pull
|
||||
uv run langgraph up --watch --no-pull
|
||||
|
||||
run_faux:
|
||||
cd graphs && poetry run langgraph up --no-pull
|
||||
cd graphs && uv run langgraph up --no-pull
|
||||
|
||||
run_graphs_reqs_a:
|
||||
cd graphs_reqs_a && poetry run langgraph up --no-pull
|
||||
cd graphs_reqs_a && uv run langgraph up --no-pull
|
||||
|
||||
run_graphs_reqs_b:
|
||||
cd graphs_reqs_b && poetry run langgraph up --no-pull
|
||||
cd graphs_reqs_b && uv run langgraph up --no-pull
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Annotated, Literal, Sequence, TypedDict
|
||||
from collections.abc import Sequence
|
||||
from typing import Annotated, Literal, TypedDict
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Annotated, List, Optional
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from langchain_community.retrievers import WikipediaRetriever
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
@@ -51,7 +51,7 @@ class Subsection(BaseModel):
|
||||
class Section(BaseModel):
|
||||
section_title: str = Field(..., title="Title of the section")
|
||||
description: str = Field(..., title="Content of the section")
|
||||
subsections: Optional[List[Subsection]] = Field(
|
||||
subsections: Optional[list[Subsection]] = Field(
|
||||
default=None,
|
||||
title="Titles and descriptions for each subsection of the Wikipedia page.",
|
||||
)
|
||||
@@ -67,7 +67,7 @@ class Section(BaseModel):
|
||||
|
||||
class Outline(BaseModel):
|
||||
page_title: str = Field(..., title="Title of the Wikipedia page")
|
||||
sections: List[Section] = Field(
|
||||
sections: list[Section] = Field(
|
||||
default_factory=list,
|
||||
title="Titles and descriptions for each section of the Wikipedia page.",
|
||||
)
|
||||
@@ -93,7 +93,7 @@ Topic of interest: {topic}
|
||||
|
||||
|
||||
class RelatedSubjects(BaseModel):
|
||||
topics: List[str] = Field(
|
||||
topics: list[str] = Field(
|
||||
description="Comprehensive list of related subjects as background research.",
|
||||
)
|
||||
|
||||
@@ -123,7 +123,7 @@ class Editor(BaseModel):
|
||||
|
||||
|
||||
class Perspectives(BaseModel):
|
||||
editors: List[Editor] = Field(
|
||||
editors: list[Editor] = Field(
|
||||
description="Comprehensive list of editors with their roles and affiliations.",
|
||||
# Add a pydantic validation/restriction to be at most M editors
|
||||
)
|
||||
@@ -200,7 +200,7 @@ def update_editor(editor, new_editor):
|
||||
|
||||
|
||||
class InterviewState(TypedDict):
|
||||
messages: Annotated[List[AnyMessage], add_messages]
|
||||
messages: Annotated[list[AnyMessage], add_messages]
|
||||
references: Annotated[Optional[dict], update_references]
|
||||
editor: Annotated[Optional[Editor], update_editor]
|
||||
|
||||
@@ -255,7 +255,7 @@ async def generate_question(state: InterviewState):
|
||||
|
||||
|
||||
class Queries(BaseModel):
|
||||
queries: List[str] = Field(
|
||||
queries: list[str] = Field(
|
||||
description="Comprehensive list of search engine queries to answer the user's questions.",
|
||||
)
|
||||
|
||||
@@ -278,7 +278,7 @@ class AnswerWithCitations(BaseModel):
|
||||
answer: str = Field(
|
||||
description="Comprehensive answer to the user's question with citations.",
|
||||
)
|
||||
cited_urls: List[str] = Field(
|
||||
cited_urls: list[str] = Field(
|
||||
description="List of urls cited in the answer.",
|
||||
)
|
||||
|
||||
@@ -437,11 +437,11 @@ class SubSection(BaseModel):
|
||||
class WikiSection(BaseModel):
|
||||
section_title: str = Field(..., title="Title of the section")
|
||||
content: str = Field(..., title="Full content of the section")
|
||||
subsections: Optional[List[Subsection]] = Field(
|
||||
subsections: Optional[list[Subsection]] = Field(
|
||||
default=None,
|
||||
title="Titles and descriptions for each subsection of the Wikipedia page.",
|
||||
)
|
||||
citations: List[str] = Field(default_factory=list)
|
||||
citations: list[str] = Field(default_factory=list)
|
||||
|
||||
@property
|
||||
def as_str(self) -> str:
|
||||
@@ -506,10 +506,10 @@ writer = writer_prompt | long_context_llm | StrOutputParser()
|
||||
class ResearchState(TypedDict):
|
||||
topic: str
|
||||
outline: Outline
|
||||
editors: List[Editor]
|
||||
interview_results: List[InterviewState]
|
||||
editors: list[Editor]
|
||||
interview_results: list[InterviewState]
|
||||
# The final sections output
|
||||
sections: List[WikiSection]
|
||||
sections: list[WikiSection]
|
||||
article: str
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Sequence, TypedDict
|
||||
from typing import Annotated, TypedDict
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Sequence, TypedDict
|
||||
from typing import Annotated, TypedDict
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-examples"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = []
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"langgraph-cli",
|
||||
"langgraph-sdk",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
langgraph-cli = { path = "../cli", editable = true }
|
||||
langgraph-sdk = { path = "../sdk_py", editable = true }
|
||||
|
||||
[tool.hatch.build]
|
||||
packages = []
|
||||
package-mode = false
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
langgraph-cli = {path = "../../cli", develop = true}
|
||||
langgraph-sdk = {path = "../../sdk-py", develop = true}
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
@@ -4,7 +4,8 @@ import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import sys
|
||||
from typing import Callable, List, Optional, Sequence, Tuple
|
||||
from collections.abc import Sequence
|
||||
from typing import Callable, Optional
|
||||
|
||||
import click
|
||||
import click.exceptions
|
||||
@@ -741,7 +742,7 @@ def prepare_args_and_stdin(
|
||||
image: Optional[str] = None,
|
||||
# Like "langchain/langgraphjs-api" or "langchain/langgraph-api
|
||||
base_image: Optional[str] = None,
|
||||
) -> Tuple[List[str], str]:
|
||||
) -> tuple[list[str], str]:
|
||||
assert config_path.exists(), f"Config file not found: {config_path}"
|
||||
# prepare args
|
||||
stdin = langgraph_cli.docker.compose(
|
||||
@@ -787,7 +788,7 @@ def prepare(
|
||||
postgres_uri: Optional[str] = None,
|
||||
image: Optional[str] = None,
|
||||
base_image: Optional[str] = None,
|
||||
) -> Tuple[List[str], str]:
|
||||
) -> tuple[list[str], str]:
|
||||
"""Prepare the arguments and stdin for running the LangGraph API server."""
|
||||
config_json = langgraph_cli.config.validate_config_file(config_path)
|
||||
# pull latest images
|
||||
|
||||
@@ -2,13 +2,13 @@ import os
|
||||
import shutil
|
||||
import sys
|
||||
from io import BytesIO
|
||||
from typing import Dict, Optional
|
||||
from typing import Optional
|
||||
from urllib import error, request
|
||||
from zipfile import ZipFile
|
||||
|
||||
import click
|
||||
|
||||
TEMPLATES: Dict[str, Dict[str, str]] = {
|
||||
TEMPLATES: dict[str, dict[str, str]] = {
|
||||
"New LangGraph Project": {
|
||||
"description": "A simple, minimal chatbot with memory.",
|
||||
"python": "https://github.com/langchain-ai/new-langgraph-project/archive/refs/heads/main.zip",
|
||||
|
||||
Generated
-2041
File diff suppressed because it is too large
Load Diff
+43
-44
@@ -1,63 +1,62 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-cli"
|
||||
version = "0.2.10"
|
||||
description = "CLI for interacting with LangGraph API"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
packages = [{ include = "langgraph_cli" }]
|
||||
license = "MIT"
|
||||
license-files = ['LICENSE']
|
||||
dependencies = [
|
||||
"click>=8.1.7",
|
||||
"langgraph-sdk>=0.1.0 ; python_version >= '3.11'",
|
||||
]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
[project.optional-dependencies]
|
||||
inmem = [
|
||||
"langgraph-api>=0.1.20 ; python_version >= '3.11'",
|
||||
"langgraph-runtime-inmem>=0.0.8 ; python_version >= '3.11'",
|
||||
"python-dotenv>=0.8.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[project.scripts]
|
||||
langgraph = "langgraph_cli.cli:cli"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
click = "^8.1.7"
|
||||
langgraph-api = { version = ">=0.1.20", optional = true, python = ">=3.11,<4.0" }
|
||||
langgraph-runtime-inmem = { version = ">=0.0.8", optional = true, python = ">=3.11,<4.0" }
|
||||
langgraph-sdk = { version = ">=0.1.0", optional = true, python = ">=3.11,<4.0" }
|
||||
python-dotenv = { version = ">=0.8.0", optional = true }
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff",
|
||||
"codespell",
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-mock",
|
||||
"pytest-watch",
|
||||
"mypy",
|
||||
"msgspec",
|
||||
]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
codespell = "^2.2.0"
|
||||
pytest = "^7.2.1"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-watch = "^4.2.0"
|
||||
mypy = "^1.10.0"
|
||||
msgspec = "^0.19.0"
|
||||
[tool.uv]
|
||||
default-groups = ['dev']
|
||||
|
||||
[tool.poetry.extras]
|
||||
inmem = ["langgraph-api", "langgraph-runtime-inmem", "python-dotenv"]
|
||||
[tool.hatch.build.targets.wheel]
|
||||
include = ["langgraph_cli"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# --strict-markers will raise errors on unknown marks.
|
||||
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
|
||||
#
|
||||
# https://docs.pytest.org/en/7.1.x/reference/reference.html
|
||||
# --strict-config any warnings encountered while parsing the `pytest`
|
||||
# section of the configuration file raise errors.
|
||||
addopts = "--strict-markers --strict-config --durations=5 -vv"
|
||||
asyncio_mode = "auto"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
# pycodestyle
|
||||
"E",
|
||||
# Pyflakes
|
||||
"F",
|
||||
# pyupgrade
|
||||
"UP",
|
||||
# flake8-bugbear
|
||||
"B",
|
||||
# isort
|
||||
"I",
|
||||
"E", # pycodestyle
|
||||
"F", # Pyflakes
|
||||
"UP", # pyupgrade
|
||||
"B", # flake8-bugbear
|
||||
"I", # isort
|
||||
]
|
||||
lint.ignore = ["E501", "B008"]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
import os
|
||||
from typing import Annotated, Sequence, TypedDict
|
||||
from collections.abc import Sequence
|
||||
from typing import Annotated, TypedDict
|
||||
|
||||
from langchain_core.language_models.fake_chat_models import FakeListChatModel
|
||||
from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage
|
||||
|
||||
Generated
+1607
File diff suppressed because it is too large
Load Diff
+21
-18
@@ -11,25 +11,28 @@ all: help
|
||||
|
||||
OUTPUT ?= out/benchmark.json
|
||||
|
||||
benchmark:
|
||||
install: ## Install dependencies
|
||||
uv sync --frozen --all-extras --all-packages --group dev
|
||||
|
||||
benchmark: .uv
|
||||
mkdir -p out
|
||||
rm -f $(OUTPUT)
|
||||
poetry run python -m bench -o $(OUTPUT) --rigorous
|
||||
uv run python -m bench -o $(OUTPUT) --rigorous
|
||||
|
||||
benchmark-fast:
|
||||
mkdir -p out
|
||||
rm -f $(OUTPUT)
|
||||
poetry run python -m bench -o $(OUTPUT) --fast
|
||||
uv run python -m bench -o $(OUTPUT) --fast
|
||||
|
||||
GRAPH ?= bench/fanout_to_subgraph.py
|
||||
|
||||
profile:
|
||||
mkdir -p out
|
||||
sudo poetry run py-spy record -g -o out/profile.svg -- python $(GRAPH)
|
||||
sudo uv run py-spy record -g -o out/profile.svg -- python $(GRAPH)
|
||||
|
||||
# Run unit tests and generate a coverage report.
|
||||
coverage:
|
||||
poetry run pytest --cov \
|
||||
uv run pytest --cov \
|
||||
--cov-config=.coveragerc \
|
||||
--cov-report xml \
|
||||
--cov-report term-missing:skip-covered
|
||||
@@ -41,7 +44,7 @@ stop-postgres:
|
||||
docker compose -f tests/compose-postgres.yml down -v
|
||||
|
||||
start-dev-server:
|
||||
poetry run langgraph dev --config tests/example_app/langgraph.json --no-browser &
|
||||
uv run langgraph dev --config tests/example_app/langgraph.json --no-browser &
|
||||
@echo "Dev server started."
|
||||
@echo "Dev server PID: $$!" > .devserver.pid
|
||||
|
||||
@@ -58,7 +61,7 @@ TEST ?= .
|
||||
test:
|
||||
make start-postgres &&\
|
||||
make start-dev-server &&\
|
||||
poetry run pytest $(TEST); \
|
||||
uv run pytest $(TEST); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-postgres; \
|
||||
make stop-dev-server; \
|
||||
@@ -67,14 +70,14 @@ test:
|
||||
test_parallel:
|
||||
make start-postgres &&\
|
||||
make start-dev-server &&\
|
||||
poetry run pytest -n auto --dist worksteal $(TEST); \
|
||||
uv run pytest -n auto --dist worksteal $(TEST); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-postgres; \
|
||||
make stop-dev-server; \
|
||||
exit $$EXIT_CODE
|
||||
|
||||
integration_tests:
|
||||
poetry run pytest integration_tests
|
||||
uv run pytest integration_tests
|
||||
|
||||
WORKERS ?= auto
|
||||
XDIST_ARGS := $(if $(WORKERS),-n $(WORKERS) --dist worksteal,)
|
||||
@@ -86,7 +89,7 @@ XDIST_ARGS := $(if $(WORKERS),-x $(XDIST_ARGS),)
|
||||
test_watch:
|
||||
make start-postgres &&\
|
||||
make start-dev-server &&\
|
||||
poetry run ptw . -- --ff -vv $(XDIST_ARGS) $(MAXFAIL_ARGS) $(TEST); \
|
||||
uv run ptw . -- --ff -vv $(XDIST_ARGS) $(MAXFAIL_ARGS) $(TEST); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-postgres; \
|
||||
make stop-dev-server; \
|
||||
@@ -110,21 +113,21 @@ lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
uv run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run mypy langgraph --cache-dir $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run mypy langgraph --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
uv run ruff format $(PYTHON_FILES)
|
||||
uv run ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
spell_check:
|
||||
poetry run codespell --toml pyproject.toml
|
||||
uv run codespell --toml pyproject.toml
|
||||
|
||||
spell_fix:
|
||||
poetry run codespell --toml pyproject.toml -w
|
||||
uv run codespell --toml pyproject.toml -w
|
||||
|
||||
|
||||
######################
|
||||
|
||||
@@ -2486,7 +2486,6 @@ class Pregel(PregelProtocol):
|
||||
CONFIG_KEY_RUNNER_SUBMIT, weakref.WeakMethod(loop.submit)
|
||||
),
|
||||
put_writes=weakref.WeakMethod(loop.put_writes),
|
||||
schedule_task=weakref.WeakMethod(loop.accept_push),
|
||||
node_finished=config[CONF].get(CONFIG_KEY_NODE_FINISHED),
|
||||
)
|
||||
# enable subgraph streaming
|
||||
@@ -2529,7 +2528,7 @@ class Pregel(PregelProtocol):
|
||||
[t for t in loop.tasks.values() if not t.writes],
|
||||
timeout=self.step_timeout,
|
||||
get_waiter=get_waiter,
|
||||
match_cached_writes=loop.match_cached_writes,
|
||||
schedule_task=loop.accept_push,
|
||||
):
|
||||
# emit output
|
||||
yield from output()
|
||||
@@ -2799,7 +2798,6 @@ class Pregel(PregelProtocol):
|
||||
CONFIG_KEY_RUNNER_SUBMIT, weakref.WeakMethod(loop.submit)
|
||||
),
|
||||
put_writes=weakref.WeakMethod(loop.put_writes),
|
||||
schedule_task=weakref.WeakMethod(loop.accept_push),
|
||||
use_astream=do_stream,
|
||||
node_finished=config[CONF].get(CONFIG_KEY_NODE_FINISHED),
|
||||
)
|
||||
@@ -2833,7 +2831,7 @@ class Pregel(PregelProtocol):
|
||||
[t for t in loop.tasks.values() if not t.writes],
|
||||
timeout=self.step_timeout,
|
||||
get_waiter=get_waiter,
|
||||
match_cached_writes=loop.amatch_cached_writes,
|
||||
schedule_task=loop.aaccept_push,
|
||||
):
|
||||
# emit output
|
||||
for o in output():
|
||||
|
||||
@@ -19,6 +19,7 @@ from typing import (
|
||||
overload,
|
||||
)
|
||||
|
||||
# meaningless change to trigger tests
|
||||
from langchain_core.callbacks import Callbacks
|
||||
from langchain_core.callbacks.manager import AsyncParentRunManager, ParentRunManager
|
||||
from langchain_core.runnables.config import RunnableConfig
|
||||
|
||||
@@ -108,7 +108,12 @@ def draw_graph(
|
||||
for w in task.writers:
|
||||
# apply regular writes
|
||||
if isinstance(w, ChannelWrite):
|
||||
w.invoke(None, task.config)
|
||||
empty_input = (
|
||||
cast(BaseChannel, specs["__root__"]).ValueType()
|
||||
if "__root__" in specs
|
||||
else None
|
||||
)
|
||||
w.invoke(empty_input, task.config)
|
||||
# apply conditional writes declared for static analysis, only once
|
||||
if w not in static_seen:
|
||||
static_seen.add(w)
|
||||
@@ -120,7 +125,7 @@ def draw_graph(
|
||||
edges.add((task.name, t[0], True, t[2]))
|
||||
writes = [t for t in writes if t[0] != END]
|
||||
conditionals.update(
|
||||
{(task.name, *t[:2]): t[2] for t in writes}
|
||||
{(task.name, t[0], t[1] or None): t[2] for t in writes}
|
||||
)
|
||||
task.config[CONF][CONFIG_KEY_SEND]([t[:2] for t in writes])
|
||||
# collect sources
|
||||
@@ -128,8 +133,8 @@ def draw_graph(
|
||||
task.name: {
|
||||
(
|
||||
w[0],
|
||||
(task.name, *w) in conditionals,
|
||||
conditionals.get((task.name, *w)),
|
||||
(task.name, w[0], w[1] or None) in conditionals,
|
||||
conditionals.get((task.name, w[0], w[1] or None)),
|
||||
)
|
||||
for w in task.writes
|
||||
}
|
||||
@@ -229,9 +234,10 @@ def draw_graph(
|
||||
first, last = graph.extend(subgraph, prefix=name)
|
||||
for idx, edge in enumerate(graph.edges):
|
||||
if edge.source == name:
|
||||
graph.edges[idx] = edge.copy(source=cast(Node, last).id)
|
||||
elif edge.target == name:
|
||||
graph.edges[idx] = edge.copy(target=cast(Node, first).id)
|
||||
edge = edge.copy(source=cast(Node, last).id)
|
||||
if edge.target == name:
|
||||
edge = edge.copy(target=cast(Node, first).id)
|
||||
graph.edges[idx] = edge
|
||||
|
||||
return graph
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ T = TypeVar("T")
|
||||
|
||||
|
||||
class Submit(Protocol[P, T]):
|
||||
def __call__(
|
||||
def __call__( # type: ignore[valid-type]
|
||||
self,
|
||||
fn: Callable[P, T],
|
||||
*args: P.args,
|
||||
|
||||
@@ -1079,6 +1079,13 @@ class SyncPregelLoop(PregelLoop, AbstractContextManager):
|
||||
matched.append(task)
|
||||
return matched
|
||||
|
||||
def accept_push(
|
||||
self, task: PregelExecutableTask, write_idx: int, call: Optional[Call] = None
|
||||
) -> Optional[PregelExecutableTask]:
|
||||
if pushed := super().accept_push(task, write_idx, call):
|
||||
self.match_cached_writes()
|
||||
return pushed
|
||||
|
||||
def put_writes(self, task_id: str, writes: WritesT) -> None:
|
||||
"""Put writes for a task, to be read by the next tick."""
|
||||
super().put_writes(task_id, writes)
|
||||
@@ -1268,6 +1275,13 @@ class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager):
|
||||
matched.append(task)
|
||||
return matched
|
||||
|
||||
async def aaccept_push(
|
||||
self, task: PregelExecutableTask, write_idx: int, call: Optional[Call] = None
|
||||
) -> Optional[PregelExecutableTask]:
|
||||
if pushed := super().accept_push(task, write_idx, call):
|
||||
await self.amatch_cached_writes()
|
||||
return pushed
|
||||
|
||||
def put_writes(self, task_id: str, writes: WritesT) -> None:
|
||||
"""Put writes for a task, to be read by the next tick."""
|
||||
super().put_writes(task_id, writes)
|
||||
|
||||
@@ -249,12 +249,12 @@ class PregelNode(Runnable):
|
||||
)
|
||||
|
||||
def join(self, channels: Sequence[str]) -> PregelNode:
|
||||
assert isinstance(channels, list) or isinstance(
|
||||
channels, tuple
|
||||
), "channels must be a list or tuple"
|
||||
assert isinstance(
|
||||
self.channels, dict
|
||||
), "all channels must be named when using .join()"
|
||||
assert isinstance(channels, list) or isinstance(channels, tuple), (
|
||||
"channels must be a list or tuple"
|
||||
)
|
||||
assert isinstance(self.channels, dict), (
|
||||
"all channels must be named when using .join()"
|
||||
)
|
||||
return self.copy(
|
||||
update=dict(
|
||||
channels={
|
||||
|
||||
@@ -39,7 +39,7 @@ from langgraph.types import (
|
||||
PregelScratchpad,
|
||||
RetryPolicy,
|
||||
)
|
||||
from langgraph.utils.future import chain_future
|
||||
from langgraph.utils.future import chain_future, run_coroutine_threadsafe
|
||||
|
||||
F = TypeVar("F", concurrent.futures.Future, asyncio.Future)
|
||||
E = TypeVar("E", threading.Event, asyncio.Event)
|
||||
@@ -119,12 +119,6 @@ class PregelRunner:
|
||||
*,
|
||||
submit: weakref.ref[Submit],
|
||||
put_writes: weakref.ref[Callable[[str, Sequence[tuple[str, Any]]], None]],
|
||||
schedule_task: weakref.ref[
|
||||
Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]],
|
||||
Optional[PregelExecutableTask],
|
||||
]
|
||||
],
|
||||
use_astream: bool = False,
|
||||
node_finished: Optional[Callable[[str], None]] = None,
|
||||
) -> None:
|
||||
@@ -132,7 +126,6 @@ class PregelRunner:
|
||||
self.put_writes = put_writes
|
||||
self.use_astream = use_astream
|
||||
self.node_finished = node_finished
|
||||
self.schedule_task = schedule_task
|
||||
|
||||
def tick(
|
||||
self,
|
||||
@@ -142,9 +135,10 @@ class PregelRunner:
|
||||
timeout: Optional[float] = None,
|
||||
retry_policy: Optional[Sequence[RetryPolicy]] = None,
|
||||
get_waiter: Optional[Callable[[], concurrent.futures.Future[None]]] = None,
|
||||
match_cached_writes: Optional[
|
||||
Callable[[], Sequence[PregelExecutableTask]]
|
||||
] = None,
|
||||
schedule_task: Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]],
|
||||
Optional[PregelExecutableTask],
|
||||
],
|
||||
) -> Iterator[None]:
|
||||
tasks = tuple(tasks)
|
||||
futures = FuturesDict(
|
||||
@@ -169,8 +163,7 @@ class PregelRunner:
|
||||
weakref.ref(t),
|
||||
retry=retry_policy,
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=self.schedule_task,
|
||||
match_cached_writes=match_cached_writes,
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
),
|
||||
@@ -212,8 +205,7 @@ class PregelRunner:
|
||||
weakref.ref(t),
|
||||
retry=retry_policy,
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=self.schedule_task,
|
||||
match_cached_writes=match_cached_writes,
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
),
|
||||
@@ -277,9 +269,10 @@ class PregelRunner:
|
||||
timeout: Optional[float] = None,
|
||||
retry_policy: Optional[Sequence[RetryPolicy]] = None,
|
||||
get_waiter: Optional[Callable[[], asyncio.Future[None]]] = None,
|
||||
match_cached_writes: Optional[
|
||||
Callable[[], Awaitable[Sequence[PregelExecutableTask]]]
|
||||
] = None,
|
||||
schedule_task: Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]],
|
||||
Awaitable[Optional[PregelExecutableTask]],
|
||||
],
|
||||
) -> AsyncIterator[None]:
|
||||
loop = asyncio.get_event_loop()
|
||||
tasks = tuple(tasks)
|
||||
@@ -307,8 +300,7 @@ class PregelRunner:
|
||||
stream=self.use_astream,
|
||||
retry=retry_policy,
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=self.schedule_task,
|
||||
match_cached_writes=match_cached_writes,
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
loop=loop,
|
||||
@@ -355,8 +347,7 @@ class PregelRunner:
|
||||
retry=retry_policy,
|
||||
stream=self.use_astream,
|
||||
futures=weakref.ref(futures),
|
||||
schedule_task=self.schedule_task,
|
||||
match_cached_writes=match_cached_writes,
|
||||
schedule_task=schedule_task,
|
||||
submit=self.submit,
|
||||
reraise=reraise,
|
||||
loop=loop,
|
||||
@@ -535,12 +526,9 @@ def _call(
|
||||
cache_policy: Optional[CachePolicy] = None,
|
||||
callbacks: Callbacks = None,
|
||||
futures: weakref.ref[FuturesDict],
|
||||
schedule_task: weakref.ref[
|
||||
Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]], Optional[PregelExecutableTask]
|
||||
]
|
||||
schedule_task: Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]], Optional[PregelExecutableTask]
|
||||
],
|
||||
match_cached_writes: Optional[Callable[[], Sequence[PregelExecutableTask]]],
|
||||
submit: weakref.ref[Submit],
|
||||
reraise: bool,
|
||||
) -> concurrent.futures.Future[Any]:
|
||||
@@ -551,13 +539,11 @@ def _call(
|
||||
# schedule PUSH tasks, collect futures
|
||||
scratchpad: PregelScratchpad = task().config[CONF][CONFIG_KEY_SCRATCHPAD] # type: ignore[union-attr]
|
||||
# schedule the next task, if the callback returns one
|
||||
if next_task := schedule_task()( # type: ignore[misc]
|
||||
if next_task := schedule_task(
|
||||
task(), # type: ignore[arg-type]
|
||||
scratchpad.call_counter(),
|
||||
Call(func, input, retry=retry, cache_policy=cache_policy, callbacks=callbacks),
|
||||
):
|
||||
if match_cached_writes:
|
||||
match_cached_writes()
|
||||
if fut := next(
|
||||
(
|
||||
f
|
||||
@@ -595,7 +581,6 @@ def _call(
|
||||
retry=retry,
|
||||
callbacks=callbacks,
|
||||
schedule_task=schedule_task,
|
||||
match_cached_writes=match_cached_writes,
|
||||
submit=submit,
|
||||
reraise=reraise,
|
||||
),
|
||||
@@ -622,106 +607,140 @@ def _acall(
|
||||
callbacks: Callbacks = None,
|
||||
# injected dependencies
|
||||
futures: weakref.ref[FuturesDict],
|
||||
schedule_task: weakref.ref[
|
||||
Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]], Optional[PregelExecutableTask]
|
||||
]
|
||||
schedule_task: Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]],
|
||||
Awaitable[Optional[PregelExecutableTask]],
|
||||
],
|
||||
match_cached_writes: Optional[
|
||||
Callable[[], Awaitable[Sequence[PregelExecutableTask]]]
|
||||
] = None,
|
||||
submit: weakref.ref[Submit],
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
reraise: bool = False,
|
||||
stream: bool = False,
|
||||
) -> Union[asyncio.Future[Any], concurrent.futures.Future[Any]]:
|
||||
fut: Optional[asyncio.Future] = None
|
||||
# schedule PUSH tasks, collect futures
|
||||
scratchpad: PregelScratchpad = task().config[CONF][CONFIG_KEY_SCRATCHPAD] # type: ignore[union-attr]
|
||||
# schedule the next task, if the callback returns one
|
||||
if next_task := schedule_task()( # type: ignore[misc]
|
||||
task(), # type: ignore[arg-type]
|
||||
scratchpad.call_counter(),
|
||||
Call(func, input, retry=retry, cache_policy=cache_policy, callbacks=callbacks),
|
||||
):
|
||||
if fut := next(
|
||||
(
|
||||
f
|
||||
for f, t in futures().items() # type: ignore[union-attr]
|
||||
if t is not None and t == next_task.id
|
||||
),
|
||||
None,
|
||||
):
|
||||
# if the parent task was retried,
|
||||
# the next task might already be running
|
||||
|
||||
pass
|
||||
elif next_task.writes:
|
||||
# if it already ran, return the result
|
||||
fut = asyncio.Future(loop=loop)
|
||||
ret = next((v for c, v in next_task.writes if c == RETURN), MISSING)
|
||||
if ret is not MISSING:
|
||||
fut.set_result(ret)
|
||||
elif exc := next((v for c, v in next_task.writes if c == ERROR), None):
|
||||
fut.set_exception(
|
||||
exc if isinstance(exc, BaseException) else Exception(exc)
|
||||
)
|
||||
else:
|
||||
fut.set_result(None)
|
||||
futures()[fut] = next_task # type: ignore[index]
|
||||
else:
|
||||
# schedule the next task
|
||||
fut = cast(
|
||||
asyncio.Future,
|
||||
submit()( # type: ignore[misc]
|
||||
arun_with_retry,
|
||||
next_task,
|
||||
retry,
|
||||
stream=stream,
|
||||
match_cached_writes=match_cached_writes,
|
||||
configurable={
|
||||
CONFIG_KEY_CALL: partial(
|
||||
_acall,
|
||||
weakref.ref(next_task),
|
||||
stream=stream,
|
||||
futures=futures,
|
||||
schedule_task=schedule_task,
|
||||
match_cached_writes=match_cached_writes,
|
||||
submit=submit,
|
||||
loop=loop,
|
||||
reraise=reraise,
|
||||
),
|
||||
},
|
||||
__name__=task().name, # type: ignore[union-attr]
|
||||
__cancel_on_exit__=True,
|
||||
__reraise_on_exit__=reraise,
|
||||
# starting a new task in the next tick ensures
|
||||
# updates from this tick are committed/streamed first
|
||||
__next_tick__=True,
|
||||
),
|
||||
)
|
||||
futures()[fut] = next_task # type: ignore[index]
|
||||
|
||||
fut = cast(Union[asyncio.Future, concurrent.futures.Future], fut)
|
||||
# return a chained future to ensure commit() callback is called
|
||||
# before the returned future is resolved, to ensure stream order etc
|
||||
try:
|
||||
in_async = asyncio.current_task() is not None
|
||||
except RuntimeError:
|
||||
in_async = False
|
||||
# if in async context return an async future
|
||||
# otherwise return a chained sync future
|
||||
# if in async context return an async future, otherwise return a sync future
|
||||
if in_async:
|
||||
if isinstance(fut, asyncio.Task):
|
||||
sfut: Union[asyncio.Future[Any], concurrent.futures.Future[Any]] = (
|
||||
asyncio.Future(loop=loop)
|
||||
)
|
||||
loop.call_soon_threadsafe(chain_future, fut, sfut)
|
||||
return sfut
|
||||
else:
|
||||
# already wrapped in a future
|
||||
return fut
|
||||
fut: Union[asyncio.Future[Any], concurrent.futures.Future[Any]] = (
|
||||
asyncio.Future(loop=loop)
|
||||
)
|
||||
else:
|
||||
sfut = concurrent.futures.Future()
|
||||
loop.call_soon_threadsafe(chain_future, fut, sfut)
|
||||
return sfut
|
||||
fut = concurrent.futures.Future()
|
||||
# schedule the next task
|
||||
run_coroutine_threadsafe(
|
||||
_acall_impl(
|
||||
fut,
|
||||
task,
|
||||
func,
|
||||
input,
|
||||
retry=retry,
|
||||
cache_policy=cache_policy,
|
||||
callbacks=callbacks,
|
||||
futures=futures,
|
||||
schedule_task=schedule_task,
|
||||
submit=submit,
|
||||
loop=loop,
|
||||
reraise=reraise,
|
||||
stream=stream,
|
||||
),
|
||||
loop,
|
||||
lazy=False,
|
||||
)
|
||||
return fut
|
||||
|
||||
|
||||
async def _acall_impl(
|
||||
destination: Union[asyncio.Future[Any], concurrent.futures.Future[Any]],
|
||||
task: weakref.ref[PregelExecutableTask],
|
||||
func: Callable[[Any], Union[Awaitable[Any], Any]],
|
||||
input: Any,
|
||||
*,
|
||||
retry: Optional[Sequence[RetryPolicy]] = None,
|
||||
cache_policy: Optional[CachePolicy] = None,
|
||||
callbacks: Callbacks = None,
|
||||
# injected dependencies
|
||||
futures: weakref.ref[FuturesDict[asyncio.Future, asyncio.Event]],
|
||||
schedule_task: Callable[
|
||||
[PregelExecutableTask, int, Optional[Call]],
|
||||
Awaitable[Optional[PregelExecutableTask]],
|
||||
],
|
||||
submit: weakref.ref[Submit],
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
reraise: bool = False,
|
||||
stream: bool = False,
|
||||
) -> None:
|
||||
try:
|
||||
fut: Optional[asyncio.Future] = None
|
||||
# schedule PUSH tasks, collect futures
|
||||
scratchpad: PregelScratchpad = task().config[CONF][CONFIG_KEY_SCRATCHPAD] # type: ignore[union-attr]
|
||||
# schedule the next task, if the callback returns one
|
||||
if next_task := await schedule_task(
|
||||
task(), # type: ignore[arg-type]
|
||||
scratchpad.call_counter(),
|
||||
Call(
|
||||
func, input, retry=retry, cache_policy=cache_policy, callbacks=callbacks
|
||||
),
|
||||
):
|
||||
if fut := next(
|
||||
(
|
||||
f
|
||||
for f, t in futures().items() # type: ignore[union-attr]
|
||||
if t is not None and t == next_task.id
|
||||
),
|
||||
None,
|
||||
):
|
||||
# if the parent task was retried,
|
||||
# the next task might already be running
|
||||
pass
|
||||
elif next_task.writes:
|
||||
# if it already ran, return the result
|
||||
fut = asyncio.Future(loop=loop)
|
||||
ret = next((v for c, v in next_task.writes if c == RETURN), MISSING)
|
||||
if ret is not MISSING:
|
||||
fut.set_result(ret)
|
||||
elif exc := next((v for c, v in next_task.writes if c == ERROR), None):
|
||||
fut.set_exception(
|
||||
exc if isinstance(exc, BaseException) else Exception(exc)
|
||||
)
|
||||
else:
|
||||
fut.set_result(None)
|
||||
futures()[fut] = next_task # type: ignore[index]
|
||||
else:
|
||||
# schedule the next task
|
||||
fut = cast(
|
||||
asyncio.Future,
|
||||
submit()( # type: ignore[misc]
|
||||
arun_with_retry,
|
||||
next_task,
|
||||
retry,
|
||||
stream=stream,
|
||||
configurable={
|
||||
CONFIG_KEY_CALL: partial(
|
||||
_acall,
|
||||
weakref.ref(next_task),
|
||||
stream=stream,
|
||||
futures=futures,
|
||||
schedule_task=schedule_task,
|
||||
submit=submit,
|
||||
loop=loop,
|
||||
reraise=reraise,
|
||||
),
|
||||
},
|
||||
__name__=task().name, # type: ignore[union-attr]
|
||||
__cancel_on_exit__=True,
|
||||
__reraise_on_exit__=reraise,
|
||||
# starting a new task in the next tick ensures
|
||||
# updates from this tick are committed/streamed first
|
||||
__next_tick__=True,
|
||||
),
|
||||
)
|
||||
futures()[fut] = next_task # type: ignore[index]
|
||||
if fut is not None:
|
||||
chain_future(fut, destination)
|
||||
else:
|
||||
destination.set_exception(RuntimeError("Task not scheduled"))
|
||||
except Exception as exc:
|
||||
destination.set_exception(exc)
|
||||
|
||||
Generated
-4112
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
[virtualenvs]
|
||||
in-project = true
|
||||
|
||||
[installer]
|
||||
modern-installation = false
|
||||
@@ -1,46 +1,65 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph"
|
||||
version = "0.4.3"
|
||||
version = "0.4.5"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
license = "MIT"
|
||||
license-files = ['LICENSE']
|
||||
dependencies = [
|
||||
"langchain-core>=0.1",
|
||||
"langgraph-checkpoint>=2.0.26",
|
||||
"langgraph-sdk>=0.1.42",
|
||||
"langgraph-prebuilt>=0.1.8",
|
||||
"xxhash>=3.5.0",
|
||||
"pydantic>=2.7.4",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
langchain-core = { version = ">=0.1", python = "<4.0" }
|
||||
langgraph-checkpoint = "^2.0.10"
|
||||
langgraph-sdk = { version = ">=0.1.42", python = "<4.0" }
|
||||
langgraph-prebuilt = { version = ">=0.1.8", python = "<4.0" }
|
||||
xxhash = "^3.5.0"
|
||||
pydantic = { version = ">=2.7.4"}
|
||||
[project.urls]
|
||||
Repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.3.2"
|
||||
pytest-cov = "^4.0.0"
|
||||
pytest-dotenv = "^0.5.2"
|
||||
pytest-mock = "^3.10.0"
|
||||
syrupy = "^4.0.2"
|
||||
httpx = "^0.26.0"
|
||||
pytest-watcher = { version = ">=0.4.1", python = "<4.0" }
|
||||
mypy = "^1.6.0"
|
||||
ruff = "^0.6.2"
|
||||
jupyter = "^1.0.0"
|
||||
pytest-xdist = {extras = ["psutil"], version = "^3.6.1"}
|
||||
pytest-repeat = "^0.9.3"
|
||||
langgraph-prebuilt = {path = "../prebuilt", develop = true}
|
||||
langgraph-checkpoint = {path = "../checkpoint", develop = true}
|
||||
langgraph-checkpoint-sqlite = {path = "../checkpoint-sqlite", develop = true}
|
||||
langgraph-checkpoint-postgres = {path = "../checkpoint-postgres", develop = true}
|
||||
langgraph-sdk = {path = "../sdk-py", develop = true}
|
||||
psycopg = {extras = ["binary"], version = ">=3.0.0", python = ">=3.10"}
|
||||
uvloop = "0.21.0beta1"
|
||||
pyperf = "^2.7.0"
|
||||
py-spy = "^0.3.14"
|
||||
types-requests = "^2.32.0.20240914"
|
||||
pycryptodome = "^3.21.0"
|
||||
langgraph-cli = {extras = ["inmem"], version = "^0.2.8"}
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8.3.2",
|
||||
"pytest-cov>=4.0.0",
|
||||
"pytest-dotenv>=0.5.2",
|
||||
"pytest-mock>=3.10.0",
|
||||
"syrupy>=4.0.2",
|
||||
"httpx>=0.26.0",
|
||||
'pytest-watcher>=0.4.1',
|
||||
"mypy>=1.6.0",
|
||||
"ruff>=0.6.2",
|
||||
"jupyter>=1.0.0",
|
||||
"pytest-xdist[psutil]>=3.6.1",
|
||||
"pytest-repeat>=0.9.3",
|
||||
"langgraph-prebuilt",
|
||||
"langgraph-checkpoint",
|
||||
"langgraph-checkpoint-sqlite",
|
||||
"langgraph-checkpoint-postgres",
|
||||
"langgraph-sdk",
|
||||
'psycopg[binary]>=3.0.0; python_version >= "3.10"',
|
||||
"uvloop==0.21.0beta1",
|
||||
"pyperf>=2.7.0",
|
||||
"py-spy>=0.3.14",
|
||||
"types-requests>=2.32.0.20240914",
|
||||
"pycryptodome>=3.21.0",
|
||||
"langgraph-cli[inmem]>=0.2.8",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
default-groups = ['dev']
|
||||
|
||||
[tool.uv.sources]
|
||||
langgraph-prebuilt = { path = "../prebuilt", editable = true }
|
||||
langgraph-checkpoint = { path = "../checkpoint", editable = true }
|
||||
langgraph-checkpoint-sqlite = { path = "../checkpoint-sqlite", editable = true }
|
||||
langgraph-checkpoint-postgres = { path = "../checkpoint-postgres", editable = true }
|
||||
langgraph-sdk = { path = "../sdk-py", editable = true }
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [ "E", "F", "I", "TID251", "UP" ]
|
||||
@@ -79,20 +98,8 @@ now = true
|
||||
delay = 0.1
|
||||
patterns = ["*.py"]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["langgraph"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# --strict-markers will raise errors on unknown marks.
|
||||
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
|
||||
#
|
||||
# https://docs.pytest.org/en/7.1.x/reference/reference.html
|
||||
# --strict-config any warnings encountered while parsing the `pytest`
|
||||
# section of the configuration file raise errors.
|
||||
#
|
||||
# https://github.com/tophat/syrupy
|
||||
# --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite.
|
||||
addopts = "--full-trace --strict-markers --strict-config --durations=5 --snapshot-warn-unused"
|
||||
# Registering custom markers.
|
||||
# https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers
|
||||
|
||||
@@ -396,6 +396,123 @@
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_get_graph_root_channel
|
||||
'''
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "__start__",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": [
|
||||
"langchain",
|
||||
"schema",
|
||||
"runnable",
|
||||
"RunnablePassthrough"
|
||||
],
|
||||
"name": "__start__"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "child",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": [
|
||||
"langgraph",
|
||||
"graph",
|
||||
"state",
|
||||
"CompiledStateGraph"
|
||||
],
|
||||
"name": "child"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "__end__"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "__start__",
|
||||
"target": "child"
|
||||
},
|
||||
{
|
||||
"source": "child",
|
||||
"target": "__end__"
|
||||
}
|
||||
]
|
||||
}
|
||||
'''
|
||||
# ---
|
||||
# name: test_get_graph_root_channel.1
|
||||
'''
|
||||
graph TD;
|
||||
__start__ --> child;
|
||||
child --> __end__;
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_get_graph_self_loop
|
||||
'''
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "__start__",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": [
|
||||
"langchain",
|
||||
"schema",
|
||||
"runnable",
|
||||
"RunnablePassthrough"
|
||||
],
|
||||
"name": "__start__"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "worker_node",
|
||||
"type": "runnable",
|
||||
"data": {
|
||||
"id": [
|
||||
"langgraph",
|
||||
"utils",
|
||||
"runnable",
|
||||
"RunnableCallable"
|
||||
],
|
||||
"name": "worker_node"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "__end__"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "__start__",
|
||||
"target": "worker_node"
|
||||
},
|
||||
{
|
||||
"source": "worker_node",
|
||||
"target": "__end__",
|
||||
"conditional": true
|
||||
},
|
||||
{
|
||||
"source": "worker_node",
|
||||
"target": "worker_node",
|
||||
"conditional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
'''
|
||||
# ---
|
||||
# name: test_get_graph_self_loop.1
|
||||
'''
|
||||
graph TD;
|
||||
__start__ --> worker_node;
|
||||
worker_node -.-> __end__;
|
||||
worker_node -.-> worker_node;
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_in_one_fan_out_state_graph_defer_node[memory-False]
|
||||
'''
|
||||
graph TD;
|
||||
|
||||
@@ -1582,9 +1582,9 @@ def test_migrate_checkpoints(source: str, target: str) -> None:
|
||||
migrated["versions_seen"][c][v].split(".")[0]
|
||||
)
|
||||
# check that the migrated checkpoint matches the target checkpoint
|
||||
assert (
|
||||
migrated == target_checkpoint.checkpoint
|
||||
), f"Checkpoint mismatch at index {idx}"
|
||||
assert migrated == target_checkpoint.checkpoint, (
|
||||
f"Checkpoint mismatch at index {idx}"
|
||||
)
|
||||
|
||||
|
||||
@NEEDS_CONTEXTVARS
|
||||
|
||||
@@ -2803,9 +2803,9 @@ def test_state_graph_packets(
|
||||
# Define decision-making logic
|
||||
def should_continue(data: dict) -> str:
|
||||
assert isinstance(data["session"], httpx.Client)
|
||||
assert (
|
||||
data["something_extra"] == "hi there"
|
||||
), "nodes can pass extra data to their cond edges, which isn't saved in state"
|
||||
assert data["something_extra"] == "hi there", (
|
||||
"nodes can pass extra data to their cond edges, which isn't saved in state"
|
||||
)
|
||||
# Logic to decide whether to continue in the loop or exit
|
||||
if tool_calls := data["messages"][-1].tool_calls:
|
||||
return [Send("tools", tool_call) for tool_call in tool_calls]
|
||||
|
||||
@@ -8727,3 +8727,45 @@ def test_get_graph_loop(snapshot: SnapshotAssertion) -> None:
|
||||
app = workflow.compile()
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
|
||||
def test_get_graph_self_loop(snapshot: SnapshotAssertion) -> None:
|
||||
import random
|
||||
|
||||
subgraph_builder = StateGraph(MessagesState)
|
||||
subgraph_builder.add_node("agent", lambda x: x)
|
||||
subgraph_builder.add_edge(START, "agent")
|
||||
subgraph = subgraph_builder.compile()
|
||||
|
||||
def worker_node(state: MessagesState) -> Command[Literal["worker_node", "__end__"]]:
|
||||
subgraph_result = subgraph.invoke(state)
|
||||
|
||||
if random.choice([True, False]):
|
||||
next_node_name = "worker_node"
|
||||
else:
|
||||
next_node_name = END
|
||||
|
||||
return Command(update=subgraph_result, goto=next_node_name)
|
||||
|
||||
self_loop_builder = StateGraph(MessagesState)
|
||||
self_loop_builder.add_node("worker_node", worker_node)
|
||||
self_loop_builder.add_edge(START, "worker_node")
|
||||
self_loop_graph = self_loop_builder.compile()
|
||||
|
||||
assert json.dumps(self_loop_graph.get_graph().to_json(), indent=2) == snapshot
|
||||
assert self_loop_graph.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
|
||||
def test_get_graph_root_channel(snapshot: SnapshotAssertion) -> None:
|
||||
child_builder = StateGraph(list)
|
||||
child_builder.add_node("child_node", lambda x: x)
|
||||
child_builder.add_edge(START, "child_node")
|
||||
child_graph = child_builder.compile()
|
||||
|
||||
graph_builder = StateGraph(list)
|
||||
graph_builder.add_node("child", child_graph)
|
||||
graph_builder.add_edge(START, "child")
|
||||
graph = graph_builder.compile()
|
||||
|
||||
assert json.dumps(graph.get_graph().to_json(), indent=2) == snapshot
|
||||
assert graph.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
@@ -863,7 +863,9 @@ async def test_ainvoke():
|
||||
assert result == {"messages": [{"type": "human", "content": "world"}]}
|
||||
|
||||
|
||||
@pytest.mark.skip("Unskip this test to manually test the LangGraph Platform integration")
|
||||
@pytest.mark.skip(
|
||||
"Unskip this test to manually test the LangGraph Platform integration"
|
||||
)
|
||||
@pytest.mark.anyio
|
||||
async def test_langgraph_cloud_integration():
|
||||
from langgraph_sdk.client import get_client, get_sync_client
|
||||
|
||||
Generated
+3460
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -16,13 +16,13 @@ stop-postgres:
|
||||
TEST ?= .
|
||||
|
||||
test:
|
||||
make start-postgres && poetry run pytest $(TEST); \
|
||||
make start-postgres && uv run pytest $(TEST); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-postgres; \
|
||||
exit $$EXIT_CODE
|
||||
|
||||
test_watch:
|
||||
make start-postgres && poetry run ptw $(TEST); \
|
||||
make start-postgres && uv run ptw $(TEST); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-postgres; \
|
||||
exit $$EXIT_CODE
|
||||
@@ -41,21 +41,21 @@ lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
uv run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run mypy langgraph --cache-dir $(MYPY_CACHE)
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run mypy langgraph --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
uv run ruff format $(PYTHON_FILES)
|
||||
uv run ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
spell_check:
|
||||
poetry run codespell --toml pyproject.toml
|
||||
uv run codespell --toml pyproject.toml
|
||||
|
||||
spell_fix:
|
||||
poetry run codespell --toml pyproject.toml -w
|
||||
uv run codespell --toml pyproject.toml -w
|
||||
|
||||
|
||||
######################
|
||||
|
||||
Generated
-1599
File diff suppressed because it is too large
Load Diff
@@ -1,46 +1,55 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.1.8"
|
||||
description = "Library with high-level APIs for creating and executing LangGraph agents and tools."
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
packages = [{ include = "langgraph" }]
|
||||
license = "MIT"
|
||||
license-files = ['LICENSE']
|
||||
dependencies = [
|
||||
"langgraph-checkpoint>=2.0.10",
|
||||
"langchain-core>=0.3.22",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
langgraph-checkpoint = "^2.0.10"
|
||||
langchain-core = { version = ">=0.3.22", python = "<4.0" }
|
||||
[project.urls]
|
||||
Repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
codespell = "^2.2.0"
|
||||
pytest = "^7.2.1"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-watcher = { version = ">=0.4.1", python = "<4.0" }
|
||||
mypy = "^1.10.0"
|
||||
langgraph = {path = "../langgraph", develop = true}
|
||||
langgraph-checkpoint = {path = "../checkpoint", develop = true}
|
||||
langgraph-checkpoint-sqlite = {path = "../checkpoint-sqlite", develop = true}
|
||||
langgraph-checkpoint-postgres = {path = "../checkpoint-postgres", develop = true}
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff",
|
||||
"codespell",
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-mock",
|
||||
"pytest-watcher",
|
||||
"mypy",
|
||||
"langgraph",
|
||||
"langgraph-checkpoint",
|
||||
"langgraph-checkpoint-sqlite",
|
||||
"langgraph-checkpoint-postgres",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
default-groups = ['dev']
|
||||
|
||||
[tool.uv.sources]
|
||||
langgraph = { path = "../langgraph", editable = true }
|
||||
langgraph-checkpoint = { path = "../checkpoint", editable = true }
|
||||
langgraph-checkpoint-sqlite = { path = "../checkpoint-sqlite", editable = true }
|
||||
langgraph-checkpoint-postgres = { path = "../checkpoint-postgres", editable = true }
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
include = ["langgraph"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# --strict-markers will raise errors on unknown marks.
|
||||
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
|
||||
#
|
||||
# https://docs.pytest.org/en/7.1.x/reference/reference.html
|
||||
# --strict-config any warnings encountered while parsing the `pytest`
|
||||
# section of the configuration file raise errors.
|
||||
addopts = "--strict-markers --strict-config --durations=5 -vv"
|
||||
asyncio_mode = "auto"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [ "E", "F", "I", "TID251" ]
|
||||
lint.ignore = [ "E501" ]
|
||||
|
||||
@@ -883,9 +883,9 @@ def test_tool_node_inject_store() -> None:
|
||||
for result in (node_result, graph_result):
|
||||
result["messages"][-1]
|
||||
tool_message = result["messages"][-1]
|
||||
assert (
|
||||
tool_message.content == "Some val: 1, store val: bar"
|
||||
), f"Failed for tool={tool_name}"
|
||||
assert tool_message.content == "Some val: 1, store val: bar", (
|
||||
f"Failed for tool={tool_name}"
|
||||
)
|
||||
|
||||
tool_call = {
|
||||
"name": "tool3",
|
||||
@@ -899,9 +899,9 @@ def test_tool_node_inject_store() -> None:
|
||||
for result in (node_result, graph_result):
|
||||
result["messages"][-1]
|
||||
tool_message = result["messages"][-1]
|
||||
assert (
|
||||
tool_message.content == "Some val: 1, store val: bar, state val: baz"
|
||||
), f"Failed for tool={tool_name}"
|
||||
assert tool_message.content == "Some val: 1, store val: bar, state val: baz", (
|
||||
f"Failed for tool={tool_name}"
|
||||
)
|
||||
|
||||
# test injected store without passing store to compiled graph
|
||||
failing_graph = builder.compile()
|
||||
|
||||
Generated
+1379
File diff suppressed because it is too large
Load Diff
@@ -13,13 +13,13 @@ stop-services:
|
||||
TEST_PATH ?= .
|
||||
|
||||
test:
|
||||
make start-services && poetry run pytest $(TEST_PATH); \
|
||||
make start-services && uv run pytest $(TEST_PATH); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-services; \
|
||||
exit $$EXIT_CODE
|
||||
|
||||
test_watch:
|
||||
make start-services && poetry run ptw . -- -x $(TEST_PATH); \
|
||||
make start-services && uv run ptw . -- -x $(TEST_PATH); \
|
||||
EXIT_CODE=$$?; \
|
||||
make stop-services; \
|
||||
exit $$EXIT_CODE
|
||||
@@ -38,11 +38,11 @@ lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
poetry run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) || poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
uv run ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || uv run ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) || uv run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
poetry run ruff format $(PYTHON_FILES)
|
||||
poetry run ruff check --select I --fix $(PYTHON_FILES)
|
||||
uv run ruff format $(PYTHON_FILES)
|
||||
uv run ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import concurrent.futures
|
||||
from typing import Optional, Sequence
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
from kafka import KafkaConsumer, KafkaProducer
|
||||
from langgraph.scheduler.kafka.types import ConsumerRecord, TopicPartition
|
||||
|
||||
@@ -221,9 +221,10 @@ class AsyncKafkaExecutor(AbstractAsyncContextManager):
|
||||
runner = PregelRunner(
|
||||
submit=weakref.ref(submit),
|
||||
put_writes=weakref.ref(put_writes),
|
||||
schedule_task=weakref.WeakMethod(self._schedule_task),
|
||||
)
|
||||
async for _ in runner.atick([task], reraise=False):
|
||||
async for _ in runner.atick(
|
||||
[task], reraise=False, schedule_task=self._schedule_task
|
||||
):
|
||||
pass
|
||||
else:
|
||||
# task was not found
|
||||
@@ -438,9 +439,10 @@ class KafkaExecutor(AbstractContextManager):
|
||||
runner = PregelRunner(
|
||||
submit=weakref.ref(submit),
|
||||
put_writes=weakref.ref(put_writes),
|
||||
schedule_task=weakref.WeakMethod(self._schedule_task),
|
||||
)
|
||||
for _ in runner.tick([task], reraise=False):
|
||||
for _ in runner.tick(
|
||||
[task], reraise=False, schedule_task=self._schedule_task
|
||||
):
|
||||
pass
|
||||
else:
|
||||
# task was not found
|
||||
|
||||
@@ -2,7 +2,8 @@ import asyncio
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
from typing import Awaitable, Callable, Optional
|
||||
from collections.abc import Awaitable
|
||||
from typing import Callable, Optional
|
||||
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
import concurrent.futures
|
||||
from typing import Any, NamedTuple, Optional, Protocol, Sequence, TypedDict, Union
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, NamedTuple, Optional, Protocol, TypedDict, Union
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
|
||||
Generated
-1692
File diff suppressed because it is too large
Load Diff
@@ -1,46 +1,54 @@
|
||||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-scheduler-kafka"
|
||||
version = "1.0.0"
|
||||
description = "Library with Kafka-based work scheduler."
|
||||
authors = []
|
||||
license = "MIT"
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
packages = [{ include = "langgraph" }]
|
||||
license = "MIT"
|
||||
license-files = ['LICENSE']
|
||||
dependencies = [
|
||||
"orjson>=3.10.7",
|
||||
"crc32c~=2.7.post1",
|
||||
"aiokafka>=0.11.0",
|
||||
"langgraph>=0.2.19",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
orjson = "^3.10.7"
|
||||
crc32c = "^2.7.post1"
|
||||
aiokafka = "^0.11.0"
|
||||
langgraph = ">=0.2.19"
|
||||
[project.urls]
|
||||
Repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
codespell = "^2.2.0"
|
||||
pytest = "^7.2.1"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-watcher = { version = ">=0.4.1", python = "<4.0" }
|
||||
mypy = "^1.10.0"
|
||||
langgraph = {path = "../langgraph", develop = true}
|
||||
langgraph-checkpoint-postgres = {path = "../checkpoint-postgres", develop = true}
|
||||
langgraph-checkpoint = {path = "../checkpoint", develop = true}
|
||||
kafka-python-ng = "^2.2.2"
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff",
|
||||
"codespell",
|
||||
"pytest",
|
||||
"pytest-mock",
|
||||
"pytest-watcher ; python_version < '4.0'",
|
||||
"mypy",
|
||||
"langgraph",
|
||||
"langgraph-checkpoint-postgres",
|
||||
"langgraph-checkpoint",
|
||||
"kafka-python-ng",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
default-groups = ['dev']
|
||||
|
||||
[tool.uv.sources]
|
||||
langgraph = { path = "../langgraph", editable = true }
|
||||
langgraph-checkpoint-postgres = { path = "../checkpoint-postgres", editable = true }
|
||||
langgraph-checkpoint = { path = "../checkpoint", editable = true }
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
include = ["langgraph"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# --strict-markers will raise errors on unknown marks.
|
||||
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
|
||||
#
|
||||
# https://docs.pytest.org/en/7.1.x/reference/reference.html
|
||||
# --strict-config any warnings encountered while parsing the `pytest`
|
||||
# section of the configuration file raise errors.
|
||||
addopts = "--strict-markers --strict-config --durations=5 -vv"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import AsyncIterator, Iterator
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
from uuid import uuid4
|
||||
|
||||
import kafka.admin
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import asyncio
|
||||
import operator
|
||||
from collections.abc import Sequence
|
||||
from typing import (
|
||||
Annotated,
|
||||
Sequence,
|
||||
TypedDict,
|
||||
Union,
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user