mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-20 14:42:28 +02:00
## Summary
Adds native uv workspace/lockfile support to the LangGraph CLI's Docker
build pipeline. Instead of listing dependencies manually, users can
point at their existing `uv.lock` and the CLI will:
1. Discover workspace packages and their dependency graph
2. Export locked requirements via `uv export --package <name> --frozen`
3. Copy only the necessary workspace closure into the container
4. Install packages in dependency order with `--no-deps` for
reproducibility
5. Rewrite all import paths (graphs, auth, encryption, etc.) to
container paths
### New config field: `source`
Rather than using `pip` or `uv pip`, we add a new `uv_lock` installer.
The previous installers should still remain unchanged.
To avoid ambiguity, we discriminate by "source" field and **do not
permit** other arbitrary "dependencies". In this mode, we will treat the
provided root (defaults to the current directory) as the source of
truth.
This also would natively support uv workspaces, so you can specify the
target package within a larger workspace.
**Simple single-package project:**
```json
{
"python_version": "3.11",
"graphs": {
"agent": "./agent.py:graph"
},
"source": {
"kind": "uv"
}
}
```
**Multi-package workspace with explicit package:**
```json
{
"python_version": "3.11",
"graphs": {
"agent": "../../apps/agent/src/agent/graph.py:graph"
},
"source": {
"kind": "uv",
"root": "../..",
"package": "agent"
}
}
```
**Traditional pip deployment (unchanged):**
```json
{
"python_version": "3.11",
"dependencies": ["langgraph", "my-package"],
"graphs": {
"agent": "./agent.py:graph"
}
}
```
Config validation enforces mutual exclusivity. you must use either
`dependencies` or `source`, not both.
---------
Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
169 lines
8.4 KiB
YAML
169 lines
8.4 KiB
YAML
name: CLI integration test
|
|
|
|
on:
|
|
workflow_call:
|
|
secrets:
|
|
LANGSMITH_API_KEY:
|
|
required: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
python-version:
|
|
- "3.10"
|
|
- "3.14"
|
|
example:
|
|
- name: A
|
|
workdir: libs/cli/examples
|
|
tag: langgraph-test-a
|
|
- name: B
|
|
workdir: libs/cli/examples/graphs
|
|
tag: langgraph-test-b
|
|
- name: C
|
|
workdir: libs/cli/examples/graphs_reqs_a
|
|
tag: langgraph-test-c
|
|
- name: D
|
|
workdir: libs/cli/examples/graphs_reqs_b
|
|
tag: langgraph-test-d
|
|
name: "CLI integration test"
|
|
env:
|
|
HAS_LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY != '' }}
|
|
defaults:
|
|
run:
|
|
working-directory: libs/cli
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- name: Get changed files
|
|
id: changed-files
|
|
if: github.event_name != 'workflow_dispatch'
|
|
uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0
|
|
with:
|
|
filter: "libs/cli/**"
|
|
- name: Set up Python ${{ matrix.python-version }}
|
|
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
|
|
uses: ./.github/actions/uv_setup
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
enable-cache: "false"
|
|
working-directory: libs/cli
|
|
- name: Install cli globally
|
|
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
|
|
run: pip install -e .
|
|
- name: Build service ${{ matrix.example.name }}
|
|
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
|
|
working-directory: ${{ matrix.example.workdir }}
|
|
run: |
|
|
langgraph build -t ${{ matrix.example.tag }}
|
|
- name: Test service ${{ matrix.example.name }}
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&env.HAS_LANGSMITH_API_KEY == 'true' }}
|
|
working-directory: ${{ matrix.example.workdir }}
|
|
env:
|
|
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
|
run: |
|
|
# Prepare environment file from local or parent example directory
|
|
if [ -f .env.example ]; then cp .env.example .env; elif [ -f ../.env.example ]; then cp ../.env.example .env && cp ../.env.example ../.env; fi
|
|
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
|
|
if [ -f ../.env ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> ../.env; fi
|
|
# Run the integration test using the built tag
|
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
timeout 60 python "$REPO_ROOT/.github/scripts/run_langgraph_cli_test.py" -t ${{ matrix.example.tag }}
|
|
|
|
- name: Build JS service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
|
|
working-directory: libs/cli/js-examples
|
|
run: |
|
|
langgraph build -t langgraph-test-e
|
|
|
|
- name: Build JS monorepo service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
|
|
working-directory: libs/cli/js-monorepo-example
|
|
run: |
|
|
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json --build-command "yarn run turbo build" --install-command "yarn install"
|
|
|
|
- name: Build Python monorepo service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
|
|
working-directory: libs/cli/python-monorepo-example
|
|
run: |
|
|
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
|
|
- name: Test Python monorepo service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
|
|
working-directory: libs/cli/python-monorepo-example
|
|
env:
|
|
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
|
run: |
|
|
cp apps/agent/.env.example apps/agent/.env
|
|
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env
|
|
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
|
|
|
|
- name: Build prerelease reqs service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
|
|
working-directory: libs/cli/examples/graph_prerelease_reqs
|
|
run: |
|
|
langgraph build -t langgraph-test-h
|
|
- name: Test prerelease reqs service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
|
|
working-directory: libs/cli/examples/graph_prerelease_reqs
|
|
env:
|
|
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
|
run: |
|
|
cp ../.env.example .env
|
|
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
|
|
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-h
|
|
echo "Finished starting up langgraph-test-h"
|
|
LANGGRAPH_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langgraph'); print(v);")
|
|
if [ "$LANGGRAPH_VERSION" != "1.1.5" ]; then
|
|
echo "LANGGRAPH_VERSION != 1.1.5; $LANGGRAPH_VERSION"
|
|
exit 1
|
|
fi
|
|
LANGCHAIN_OPENAI_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-openai'); print(v);")
|
|
if [ "$LANGCHAIN_OPENAI_VERSION" != "1.0.1" ]; then
|
|
echo "LANGCHAIN_OPENAI_VERSION != 1.0.1; $LANGCHAIN_OPENAI_VERSION"
|
|
exit 1
|
|
fi
|
|
LANGCHAIN_ANTHROPIC_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-anthropic'); print(v);")
|
|
if [ "$LANGCHAIN_ANTHROPIC_VERSION" != "1.0.0a5" ]; then
|
|
echo "LANGCHAIN_ANTHROPIC_VERSION != 1.0.0a5; $LANGCHAIN_ANTHROPIC_VERSION"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Build and test prerelease reqs fail service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
|
|
working-directory: libs/cli/examples/graph_prerelease_reqs_fail
|
|
run: |
|
|
langgraph build -t langgraph-test-i || [ $? -eq 1 ]
|
|
|
|
- name: Build uv simple service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
|
|
working-directory: libs/cli/uv-examples/simple
|
|
run: |
|
|
langgraph build -t langgraph-test-uv-simple
|
|
- name: Test uv simple service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
|
|
working-directory: libs/cli/uv-examples/simple
|
|
env:
|
|
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
|
run: |
|
|
cp .env.example .env
|
|
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
|
|
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-uv-simple
|
|
|
|
- name: Build uv monorepo service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
|
|
working-directory: libs/cli/uv-examples/monorepo/apps/agent
|
|
run: |
|
|
langgraph build -t langgraph-test-uv-monorepo
|
|
- name: Test uv monorepo service
|
|
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
|
|
working-directory: libs/cli/uv-examples/monorepo/apps/agent
|
|
env:
|
|
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
|
run: |
|
|
cp .env.example .env
|
|
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
|
|
timeout 60 python ../../../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-uv-monorepo
|