mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-10-02 06:25:09 +02:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08d47daa90 | ||
|
|
682f39e0d3 | ||
|
|
7bbe8d8628 | ||
|
|
4dfd1c368c | ||
|
|
b75daf093e | ||
|
|
faacbc1570 | ||
|
|
f5f536ba78 | ||
|
|
7284326160 | ||
|
|
62f7548532 | ||
|
|
e6a9e1d1c1 | ||
|
|
94fa329100 | ||
|
|
264caa0684 | ||
|
|
cd33de2ad1 | ||
|
|
692f177a10 | ||
|
|
14d4be6c4a | ||
|
|
2e133d6189 | ||
|
|
89de950307 | ||
|
|
788b62c0fb | ||
|
|
1539a55d2c | ||
|
|
f3055178f3 | ||
|
|
c0067cd304 | ||
|
|
b90d7c4e58 | ||
|
|
2c14b1d658 | ||
|
|
50ce6badee | ||
|
|
6fc5b3aeda | ||
|
|
b543752878 | ||
|
|
8f6ad0b25a | ||
|
|
ada5d2ecb1 | ||
|
|
eaeafe54ab | ||
|
|
f761116de7 | ||
|
|
36cf353d19 | ||
|
|
d503c0bf33 | ||
|
|
25ba4c3bda | ||
|
|
dfc1c59ebf | ||
|
|
6f4c5fefee | ||
|
|
7cf230defa | ||
|
|
5db65e0281 | ||
|
|
b08c2e092f | ||
|
|
120ae38c12 | ||
|
|
22942d4eec | ||
|
|
1756ce1dd2 | ||
|
|
0b4638269b | ||
|
|
1ebdb1ba31 | ||
|
|
f3423c052e | ||
|
|
b63572ee16 | ||
|
|
ddf4e62bde |
@@ -19,7 +19,7 @@ jobs:
|
||||
run:
|
||||
working-directory: libs/cli
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
@@ -87,3 +87,27 @@ jobs:
|
||||
working-directory: libs/cli/js-examples
|
||||
run: |
|
||||
langgraph build -t langgraph-test-e
|
||||
|
||||
- name: Build JS monorepo service
|
||||
if: steps.changed-files.outputs.all
|
||||
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
|
||||
working-directory: libs/cli/python-monorepo-example
|
||||
run: |
|
||||
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
cp apps/agent/.env.example apps/agent/.env
|
||||
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env; fi
|
||||
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
|
||||
- name: Build and test prerelease reqs service
|
||||
if: steps.changed-files.outputs.all
|
||||
working-directory: libs/cli/examples/graph_prerelease_reqs
|
||||
run: |
|
||||
langgraph build -t langgraph-test-h
|
||||
cp ../.env.example .env
|
||||
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env; fi
|
||||
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-h
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
- "3.12"
|
||||
name: "lint #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
working-directory: libs/langgraph
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
version: ${{ steps.check-version.outputs.version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python $${ env.PYTHON_VERSION }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
@@ -75,9 +75,9 @@ jobs:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: test-dist
|
||||
path: ${{ inputs.working-directory }}/dist/
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
run:
|
||||
working-directory: libs/langgraph
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- run: SHA=$(git rev-parse HEAD) && echo "SHA=$SHA" >> $GITHUB_ENV
|
||||
- name: Set up Python 3.11
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
run:
|
||||
working-directory: libs/langgraph
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- id: files
|
||||
name: Get changed files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
python: ${{ steps.filter.outputs.python }}
|
||||
deps: ${{ steps.filter.outputs.deps }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
@@ -100,9 +100,9 @@ jobs:
|
||||
name: "Check SDK methods matching"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: Run check_sdk_methods script
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
python-version:
|
||||
- "3.11"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
outputs:
|
||||
changed-files: ${{ steps.changed-files.outputs.added_modified }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.MKDOCS_GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
|
||||
- name: Upload Pages Artifact
|
||||
# if: github.ref == 'refs/heads/main'
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: ./docs/site/
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate PR Title
|
||||
uses: amannn/action-semantic-pull-request@v5
|
||||
uses: amannn/action-semantic-pull-request@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@@ -40,6 +40,7 @@ jobs:
|
||||
sdk-py
|
||||
docs
|
||||
ci
|
||||
deps
|
||||
requireScope: false
|
||||
ignoreLabels: |
|
||||
ignore-lint-pr-title
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
tag: ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
outputs:
|
||||
release-body: ${{ steps.generate-release-body.outputs.release-body }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
repository: langchain-ai/langgraph
|
||||
path: langgraph
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
- test-pypi-publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
# We explicitly *don't* set up caching here. This ensures our tests are
|
||||
# maximally sensitive to catching breakage.
|
||||
@@ -261,7 +261,7 @@ jobs:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
@@ -270,7 +270,7 @@ jobs:
|
||||
enable-cache: true
|
||||
cache-suffix: "release"
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: dist
|
||||
path: ${{ inputs.working-directory }}/dist/
|
||||
@@ -302,7 +302,7 @@ jobs:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
@@ -311,7 +311,7 @@ jobs:
|
||||
enable-cache: true
|
||||
cache-suffix: "release"
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: dist
|
||||
path: ${{ inputs.working-directory }}/dist/
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- "latest"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python + Poetry
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
@@ -71,7 +71,7 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
|
||||
|
||||
## Additional resources
|
||||
|
||||
- [Guides](https://langchain-ai.github.io/langgraph/how-tos/): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
|
||||
- [Guides](https://langchain-ai.github.io/langgraph/guides/): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
|
||||
- [Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Detailed reference on core classes, methods, how to use the graph and checkpointing APIs, and higher-level prebuilt components.
|
||||
- [Examples](https://langchain-ai.github.io/langgraph/examples/): Guided examples on getting started with LangGraph.
|
||||
- [LangChain Forum](https://forum.langchain.com/): Connect with the community and share all of your technical questions, ideas, and feedback.
|
||||
|
||||
Binary file not shown.
@@ -2108,9 +2108,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"hono@npm:^4.5.4":
|
||||
version: 4.8.9
|
||||
resolution: "hono@npm:4.8.9"
|
||||
checksum: 10c0/385539d1787fdc747bc869ef0e5ccc9f39cbe40289b94f23eecfc82c6ca440f059704647cd6381a5066d2cf7baa43ab25184c78d44af4c5c98a5c5b07670059e
|
||||
version: 4.9.6
|
||||
resolution: "hono@npm:4.9.6"
|
||||
checksum: 10c0/182a144eb3b9e05bd9e43d15af15c93f60d3d747fef6c6904b9993e9db8129ea7fadf6190331d6f76b1bf6dd2b2c3b13efea105236f541ef411397e30475422d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ graph.invoke( # (1)!
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
# highlight-next-line
|
||||
def node(state: State, config: Runtime[ContextSchema]):
|
||||
def node(state: State, runtime: Runtime[ContextSchema]):
|
||||
user_name = runtime.context.user_name
|
||||
...
|
||||
```
|
||||
|
||||
@@ -99,8 +99,8 @@ Starting from the `LangGraph Platform` view...
|
||||
1. In the top-right corner, select the gear icon (`Deployment Settings`).
|
||||
1. Update the `Git Branch` to the desired branch.
|
||||
1. Check/uncheck checkbox to `Automatically update deployment on push to branch`.
|
||||
1. Branch creation/deletion and tag creation/deletion events will not trigger an update. Only pushes to an existing branch will trigger an update.
|
||||
1. Pushes in quick succession to a branch will not trigger subsequent updates. In the future, this functionality may be changed/improved.
|
||||
1. Branch creation/deletion and tag creation/deletion events will not trigger an update. Only pushes to an existing branch will trigger an update.
|
||||
1. Pushes in quick succession to a branch will queue subsequent updates. Once a build completes, the most recent commit will begin building and the other queued builds will be skipped.
|
||||
|
||||
## Add or Remove GitHub Repositories
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
"name": "Store",
|
||||
"description": "Store is an API for managing persistent key-value store (long-term memory) that is available from any thread."
|
||||
},
|
||||
{
|
||||
"name": "A2A",
|
||||
"description": "Agent-to-Agent Protocol related endpoints for exposing assistants as A2A-compliant agents."
|
||||
},
|
||||
{
|
||||
"name": "MCP",
|
||||
"description": "Model Context Protocol related endpoints for exposing an agent as an MCP server."
|
||||
@@ -1520,6 +1524,96 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/threads/{thread_id}/stream": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Threads"
|
||||
],
|
||||
"summary": "Join Thread Stream",
|
||||
"description": "This endpoint streams output in real-time from a thread. The stream will include the output of each run executed sequentially on the thread and will remain open indefinitely. It is the responsibility of the calling client to close the connection.",
|
||||
"operationId": "join_thread_stream_threads__thread_id__stream_get",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "The ID of the thread.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Thread Id",
|
||||
"description": "The ID of the thread."
|
||||
},
|
||||
"name": "thread_id",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"title": "Last Event ID",
|
||||
"description": "The ID of the last event received. Used to resume streaming from a specific point. Pass '-' to resume from the beginning."
|
||||
},
|
||||
"name": "Last-Event-ID",
|
||||
"in": "header"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["lifecycle", "run_modes", "state_update"]
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["lifecycle", "run_modes", "state_update"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"default": ["run_modes"],
|
||||
"title": "Stream Modes",
|
||||
"description": "Stream modes to control which events are returned. 'lifecycle' returns only run start/end events, 'run_modes' returns all run events (default behavior), 'state_update' returns only state update events."
|
||||
},
|
||||
"name": "stream_modes",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"description": "The server will send a stream of events in SSE format.\n\n**Example event**:\n\nid: 1\n\nevent: message\n\ndata: {}"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/threads/{thread_id}/runs": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -3092,6 +3186,195 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/a2a/{assistant_id}": {
|
||||
"post": {
|
||||
"operationId": "post_a2a",
|
||||
"summary": "A2A Post",
|
||||
"description": "Communicate with an assistant using the Agent-to-Agent Protocol.\nSends a JSON-RPC 2.0 message to the assistant.\n\n- **Request**: Provide an object with `jsonrpc`, `id`, `method`, and optional `params`.\n- **Response**: Returns a JSON-RPC response with task information or error.\n\n**Supported Methods:**\n- `message/send`: Send a message to the assistant\n- `tasks/get`: Get the status and result of a task\n\n**Notes:**\n- Supports threaded conversations via thread context\n- Messages can contain text and data parts\n- Tasks run asynchronously and return completion status\n",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "assistant_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"description": "The ID of the assistant to communicate with"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["application/json"]
|
||||
},
|
||||
"description": "Must be application/json"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jsonrpc": {
|
||||
"type": "string",
|
||||
"enum": ["2.0"],
|
||||
"description": "JSON-RPC version"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Request identifier"
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["message/send", "tasks/get"],
|
||||
"description": "The method to invoke"
|
||||
},
|
||||
"params": {
|
||||
"type": "object",
|
||||
"description": "Method parameters",
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Message Send Parameters",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"enum": ["user", "assistant"],
|
||||
"description": "Message role"
|
||||
},
|
||||
"parts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Text Part",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["text"]
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["kind", "text"]
|
||||
},
|
||||
{
|
||||
"title": "Data Part",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["data"]
|
||||
},
|
||||
"data": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": ["kind", "data"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Message parts"
|
||||
},
|
||||
"messageId": {
|
||||
"type": "string",
|
||||
"description": "Unique message identifier"
|
||||
}
|
||||
},
|
||||
"required": ["role", "parts", "messageId"]
|
||||
},
|
||||
"thread": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"threadId": {
|
||||
"type": "string",
|
||||
"description": "Thread identifier for conversation context"
|
||||
}
|
||||
},
|
||||
"description": "Optional thread context"
|
||||
}
|
||||
},
|
||||
"required": ["message"]
|
||||
},
|
||||
{
|
||||
"title": "Task Get Parameters",
|
||||
"properties": {
|
||||
"taskId": {
|
||||
"type": "string",
|
||||
"description": "Task identifier to retrieve"
|
||||
}
|
||||
},
|
||||
"required": ["taskId"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["jsonrpc", "id", "method"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "JSON-RPC response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jsonrpc": {
|
||||
"type": "string",
|
||||
"enum": ["2.0"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"result": {
|
||||
"type": "object",
|
||||
"description": "Success result containing task information or task details"
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Error information if request failed"
|
||||
}
|
||||
},
|
||||
"required": ["jsonrpc", "id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request - invalid JSON-RPC or missing Accept header"
|
||||
},
|
||||
"404": {
|
||||
"description": "Assistant not found"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"A2A"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/mcp/": {
|
||||
"post": {
|
||||
"operationId": "post_mcp",
|
||||
@@ -4346,6 +4629,17 @@
|
||||
"title": "Checkpoint During",
|
||||
"description": "Whether to checkpoint during the run.",
|
||||
"default": false
|
||||
},
|
||||
"durability": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sync",
|
||||
"async",
|
||||
"exit"
|
||||
],
|
||||
"title": "Durability",
|
||||
"description": "Durability level for the run. Must be one of 'sync', 'async', or 'exit'.",
|
||||
"default": "async"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -4582,6 +4876,17 @@
|
||||
"title": "Checkpoint During",
|
||||
"description": "Whether to checkpoint during the run.",
|
||||
"default": false
|
||||
},
|
||||
"durability": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sync",
|
||||
"async",
|
||||
"exit"
|
||||
],
|
||||
"title": "Durability",
|
||||
"description": "Durability level for the run. Must be one of 'sync', 'async', or 'exit'.",
|
||||
"default": "async"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -4710,6 +5015,12 @@
|
||||
},
|
||||
"ThreadSearchRequest": {
|
||||
"properties": {
|
||||
"ids": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "format": "uuid"},
|
||||
"title": "Ids",
|
||||
"description": "List of thread IDs to include. Others are excluded."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"title": "Metadata",
|
||||
@@ -4950,11 +5261,30 @@
|
||||
"type": "object",
|
||||
"title": "Metadata",
|
||||
"description": "Metadata to merge with existing thread metadata."
|
||||
},
|
||||
"ttl": {
|
||||
"type": "object",
|
||||
"title": "TTL",
|
||||
"description": "The time-to-live for the thread.",
|
||||
"properties": {
|
||||
"strategy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"delete"
|
||||
],
|
||||
"description": "The TTL strategy. 'delete' removes the entire thread.",
|
||||
"default": "delete"
|
||||
},
|
||||
"ttl": {
|
||||
"type": "number",
|
||||
"description": "The time-to-live in minutes from now until thread should be swept."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "ThreadPatch",
|
||||
"description": "Payload for creating a thread."
|
||||
"description": "Payload for updating a thread."
|
||||
},
|
||||
"ThreadStateCheckpointRequest": {
|
||||
"properties": {
|
||||
|
||||
@@ -483,19 +483,19 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt langchain_community langchain_anthropic langchain_openai wikipedia scikit-learn
|
||||
|
||||
ADD ./graphs /deps/__outer_graphs/src
|
||||
ADD ./graphs /deps/outer-graphs/src
|
||||
RUN set -ex && \
|
||||
for line in '[project]' \
|
||||
'name = "graphs"' \
|
||||
'version = "0.1"' \
|
||||
'[tool.setuptools.package-data]' \
|
||||
'"*" = ["**/*"]'; do \
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \
|
||||
done
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-graphs/src/agent.py:graph", "storm": "/deps/outer-graphs/src/storm.py:graph"}'
|
||||
```
|
||||
|
||||
???+ note "Updating your langgraph.json file"
|
||||
|
||||
@@ -1040,7 +1040,7 @@ def node_a(state: State, runtime: Runtime[ContextSchema]):
|
||||
...
|
||||
```
|
||||
|
||||
See [this guide](../how-tos/graph-api.ipynb#add-runtime-configuration) for a full breakdown on configuration.
|
||||
See [this guide](../how-tos/graph-api.md#add-runtime-configuration) for a full breakdown on configuration.
|
||||
:::
|
||||
|
||||
:::js
|
||||
|
||||
@@ -134,7 +134,7 @@ def update_instructions(state: State, store: BaseStore):
|
||||
namespace = ("instructions",)
|
||||
current_instructions = store.search(namespace)[0]
|
||||
# Memory logic
|
||||
prompt = prompt_template.format(instructions=instructions.value["instructions"], conversation=state["messages"])
|
||||
prompt = prompt_template.format(instructions=current_instructions.value["instructions"], conversation=state["messages"])
|
||||
output = llm.invoke(prompt)
|
||||
new_instructions = output['new_instructions']
|
||||
store.put(("agent_instructions",), "agent_a", {"instructions": new_instructions})
|
||||
@@ -278,4 +278,4 @@ const items = await store.search(
|
||||
```
|
||||
:::
|
||||
|
||||
For more information about the memory store, see the [Persistence](persistence.md#memory-store) guide.
|
||||
For more information about the memory store, see the [Persistence](persistence.md#memory-store) guide.
|
||||
|
||||
@@ -897,5 +897,5 @@ There are two high-level approaches to achieve that:
|
||||
|
||||
An agent might need to have a different state schema from the rest of the agents. For example, a search agent might only need to keep track of queries and retrieved documents. There are two ways to achieve this in LangGraph:
|
||||
|
||||
- Define [subgraph](./subgraphs.md) agents with a separate state schema. If there are no shared state keys (channels) between the subgraph and the parent graph, it's important to [add input / output transformations](../how-tos/subgraph.ipynb#different-state-schemas) so that the parent graph knows how to communicate with the subgraphs.
|
||||
- Define agent node functions with a [private input state schema](../how-tos/graph-api.ipynb#pass-private-state-between-nodes) that is distinct from the overall graph state schema. This allows passing information that is only needed for executing that particular agent.
|
||||
- Define [subgraph](./subgraphs.md) agents with a separate state schema. If there are no shared state keys (channels) between the subgraph and the parent graph, it's important to [add input / output transformations](../how-tos/subgraph.md#different-state-schemas) so that the parent graph knows how to communicate with the subgraphs.
|
||||
- Define agent node functions with a [private input state schema](../how-tos/graph-api.md#pass-private-state-between-nodes) that is distinct from the overall graph state schema. This allows passing information that is only needed for executing that particular agent.
|
||||
|
||||
@@ -1019,7 +1019,7 @@ console.log(await graph.invoke({}, { configurable: { myRuntimeValue: "b" } }));
|
||||
# Usage
|
||||
input_message = {"role": "user", "content": "hi"}
|
||||
# With no configuration, uses default (Anthropic)
|
||||
response_1 = graph.invoke({"messages": [input_message]})["messages"][-1]
|
||||
response_1 = graph.invoke({"messages": [input_message]}, context=ContextSchema())["messages"][-1]
|
||||
# Or, can set OpenAI
|
||||
response_2 = graph.invoke({"messages": [input_message]}, context={"model_provider": "openai"})["messages"][-1]
|
||||
|
||||
@@ -1205,7 +1205,7 @@ There are many use cases where you may wish for your node to have a custom retry
|
||||
To configure a retry policy, pass the `retry_policy` parameter to the [add_node](../reference/graphs.md#langgraph.graph.state.StateGraph.add_node). The `retry_policy` parameter takes in a `RetryPolicy` named tuple object. Below we instantiate a `RetryPolicy` object with the default parameters and associate it with a node:
|
||||
|
||||
```python
|
||||
from langgraph.pregel import RetryPolicy
|
||||
from langgraph.types import RetryPolicy
|
||||
|
||||
builder.add_node(
|
||||
"node_name",
|
||||
@@ -1260,7 +1260,7 @@ By default, the retry policy retries on any exception except for the following:
|
||||
from typing_extensions import TypedDict
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langgraph.graph import END, MessagesState, StateGraph, START
|
||||
from langgraph.pregel import RetryPolicy
|
||||
from langgraph.types import RetryPolicy
|
||||
from langchain_community.utilities import SQLDatabase
|
||||
from langchain_core.messages import AIMessage
|
||||
|
||||
@@ -1422,15 +1422,15 @@ const builder = new StateGraph(State)
|
||||
:::
|
||||
|
||||
??? info "Why split application steps into a sequence with LangGraph?"
|
||||
LangGraph makes it easy to add an underlying persistence layer to your application.
|
||||
This allows state to be checkpointed in between the execution of nodes, so your LangGraph nodes govern:
|
||||
LangGraph makes it easy to add an underlying persistence layer to your application.
|
||||
This allows state to be checkpointed in between the execution of nodes, so your LangGraph nodes govern:
|
||||
|
||||
- How state updates are [checkpointed](../concepts/persistence.md)
|
||||
- How interruptions are resumed in [human-in-the-loop](../concepts/human_in_the_loop.md) workflows
|
||||
- How we can "rewind" and branch-off executions using LangGraph's [time travel](../concepts/time-travel.md) features
|
||||
- How state updates are [checkpointed](../concepts/persistence.md)
|
||||
- How interruptions are resumed in [human-in-the-loop](../concepts/human_in_the_loop.md) workflows
|
||||
- How we can "rewind" and branch-off executions using LangGraph's [time travel](../concepts/time-travel.md) features
|
||||
|
||||
They also determine how execution steps are [streamed](../concepts/streaming.md), and how your application is visualized
|
||||
and debugged using [LangGraph Studio](../concepts/langgraph_studio.md).
|
||||
They also determine how execution steps are [streamed](../concepts/streaming.md), and how your application is visualized
|
||||
and debugged using [LangGraph Studio](../concepts/langgraph_studio.md).
|
||||
|
||||
Let's demonstrate an end-to-end example. We will create a sequence of three steps:
|
||||
|
||||
@@ -2110,7 +2110,6 @@ builder.add_edge(START, "generate_topics")
|
||||
builder.add_conditional_edges("generate_topics", continue_to_jokes, ["generate_joke"])
|
||||
builder.add_edge("generate_joke", "best_joke")
|
||||
builder.add_edge("best_joke", END)
|
||||
builder.add_edge("generate_topics", END)
|
||||
graph = builder.compile()
|
||||
```
|
||||
|
||||
@@ -2333,7 +2332,7 @@ from IPython.display import Image, display
|
||||
display(Image(graph.get_graph().draw_mermaid_png()))
|
||||
```
|
||||
|
||||

|
||||

|
||||
:::
|
||||
|
||||
:::js
|
||||
@@ -3272,7 +3271,7 @@ from langchain_core.runnables.graph import CurveStyle, MermaidDrawMethod, NodeSt
|
||||
display(Image(app.get_graph().draw_mermaid_png()))
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
**Using Mermaid + Pyppeteer**
|
||||
|
||||
@@ -3320,4 +3319,4 @@ const imageBuffer = new Uint8Array(await image.arrayBuffer());
|
||||
|
||||
await fs.writeFile("graph.png", imageBuffer);
|
||||
```
|
||||
:::
|
||||
:::
|
||||
|
||||
@@ -366,8 +366,8 @@ result = graph.invoke(
|
||||
|
||||
# Resume with mapping of interrupt IDs to values
|
||||
resume_map = {
|
||||
i.interrupt_id: f"human input for prompt {i.value}"
|
||||
for i in parent.get_state(thread_config).interrupts
|
||||
i.id: f"edited text for {i.value['text_to_revise']}"
|
||||
for i in graph.get_state(config).interrupts
|
||||
}
|
||||
print(graph.invoke(Command(resume=resume_map), config=config))
|
||||
# > {'text_1': 'edited text for original text 1', 'text_2': 'edited text for original text 2'}
|
||||
|
||||
@@ -1948,7 +1948,7 @@ const llmWithTools = llm.bindTools(tools);
|
||||
|
||||
|
||||
# Conditional edge function to route to the tool node or end based upon whether the LLM made a tool call
|
||||
def should_continue(state: MessagesState) -> Literal["environment", END]:
|
||||
def should_continue(state: MessagesState) -> Literal["Action", END]:
|
||||
"""Decide if we should continue the loop or stop based upon whether the LLM made a tool call"""
|
||||
|
||||
messages = state["messages"]
|
||||
|
||||
@@ -707,7 +707,9 @@
|
||||
" \"\"\"\n",
|
||||
" Find all tool calls in the messages returned\n",
|
||||
" \"\"\"\n",
|
||||
" tool_calls = [tc['name'] for m in messages['messages'] for tc in getattr(m, 'tool_calls', [])]\n",
|
||||
" tool_calls = [\n",
|
||||
" tc[\"name\"] for m in messages[\"messages\"] for tc in getattr(m, \"tool_calls\", [])\n",
|
||||
" ]\n",
|
||||
" return tool_calls\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -861,3 +861,46 @@ def test_store_ttl(store):
|
||||
# Now has been (TTL_SECONDS-2)*2 > TTL_SECONDS + TTL_SECONDS/2
|
||||
res = store.search(ns, query="bar", refresh_ttl=False)
|
||||
assert len(res) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"vector_type,distance_type",
|
||||
[
|
||||
("vector", "cosine"),
|
||||
("vector", "inner_product"),
|
||||
("halfvec", "cosine"),
|
||||
("halfvec", "inner_product"),
|
||||
],
|
||||
)
|
||||
def test_non_ascii(
|
||||
request: Any,
|
||||
fake_embeddings: CharacterEmbeddings,
|
||||
vector_type: str,
|
||||
distance_type: str,
|
||||
) -> None:
|
||||
"""Test support for non-ascii characters"""
|
||||
with _create_vector_store(
|
||||
vector_type,
|
||||
distance_type,
|
||||
fake_embeddings
|
||||
) as store:
|
||||
|
||||
store.put(("user_123", "memories"), "1", {"text": "这是中文"}) # Chinese
|
||||
store.put(
|
||||
("user_123", "memories"), "2", {"text": "これは日本語です"}
|
||||
) # Japanese
|
||||
store.put(("user_123", "memories"), "3", {"text": "이건 한국어야"}) # Korean
|
||||
store.put(("user_123", "memories"), "4", {"text": "Это русский"}) # Russian
|
||||
store.put(("user_123", "memories"), "5", {"text": "यह रूसी है"}) # Hindi
|
||||
|
||||
result1 = store.search(("user_123", "memories"), query="这是中文")
|
||||
result2 = store.search(("user_123", "memories"), query="これは日本語です")
|
||||
result3 = store.search(("user_123", "memories"), query="이건 한국어야")
|
||||
result4 = store.search(("user_123", "memories"), query="Это русский")
|
||||
result5 = store.search(("user_123", "memories"), query="यह रूसी है")
|
||||
|
||||
assert result1[0].key == "1"
|
||||
assert result2[0].key == "2"
|
||||
assert result3[0].key == "3"
|
||||
assert result4[0].key == "4"
|
||||
assert result5[0].key == "5"
|
||||
|
||||
@@ -1067,3 +1067,31 @@ def test_sql_injection_vulnerability(store: SqliteStore) -> None:
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid filter key"):
|
||||
store.search(("docs",), filter={malicious_key: "dummy"})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("distance_type", VECTOR_TYPES)
|
||||
def test_non_ascii(
|
||||
fake_embeddings: CharacterEmbeddings,
|
||||
distance_type: str,
|
||||
) -> None:
|
||||
"""Test support for non-ascii characters"""
|
||||
with create_vector_store(fake_embeddings, distance_type=distance_type) as store:
|
||||
store.put(("user_123", "memories"), "1", {"text": "这是中文"}) # Chinese
|
||||
store.put(
|
||||
("user_123", "memories"), "2", {"text": "これは日本語です"}
|
||||
) # Japanese
|
||||
store.put(("user_123", "memories"), "3", {"text": "이건 한국어야"}) # Korean
|
||||
store.put(("user_123", "memories"), "4", {"text": "Это русский"}) # Russian
|
||||
store.put(("user_123", "memories"), "5", {"text": "यह रूसी है"}) # Hindi
|
||||
|
||||
result1 = store.search(("user_123", "memories"), query="这是中文")
|
||||
result2 = store.search(("user_123", "memories"), query="これは日本語です")
|
||||
result3 = store.search(("user_123", "memories"), query="이건 한국어야")
|
||||
result4 = store.search(("user_123", "memories"), query="Это русский")
|
||||
result5 = store.search(("user_123", "memories"), query="यह रूसी है")
|
||||
|
||||
assert result1[0].key == "1"
|
||||
assert result2[0].key == "2"
|
||||
assert result3[0].key == "3"
|
||||
assert result4[0].key == "4"
|
||||
assert result5[0].key == "5"
|
||||
|
||||
@@ -238,7 +238,7 @@ def get_text_at_path(obj: Any, path: str | list[str]) -> list[str]:
|
||||
- Nested paths in multi-field: "{field1,nested.field2}"
|
||||
"""
|
||||
if not path or path == "$":
|
||||
return [json.dumps(obj, sort_keys=True)]
|
||||
return [json.dumps(obj, sort_keys=True, ensure_ascii=False)]
|
||||
|
||||
tokens = tokenize_path(path) if isinstance(path, str) else path
|
||||
|
||||
@@ -249,7 +249,7 @@ def get_text_at_path(obj: Any, path: str | list[str]) -> list[str]:
|
||||
elif obj is None:
|
||||
return []
|
||||
elif isinstance(obj, (list, dict)):
|
||||
return [json.dumps(obj, sort_keys=True)]
|
||||
return [json.dumps(obj, sort_keys=True, ensure_ascii=False)]
|
||||
return []
|
||||
|
||||
token = tokens[pos]
|
||||
@@ -295,7 +295,11 @@ def get_text_at_path(obj: Any, path: str | list[str]) -> list[str]:
|
||||
if isinstance(current_obj, (str, int, float, bool)):
|
||||
results.append(str(current_obj))
|
||||
elif isinstance(current_obj, (list, dict)):
|
||||
results.append(json.dumps(current_obj, sort_keys=True))
|
||||
results.append(
|
||||
json.dumps(
|
||||
current_obj, sort_keys=True, ensure_ascii=False
|
||||
)
|
||||
)
|
||||
|
||||
# Handle wildcard
|
||||
elif token == "*":
|
||||
|
||||
@@ -5,12 +5,13 @@ import time
|
||||
import pytest
|
||||
import redis
|
||||
|
||||
from langgraph.cache.base import FullKey
|
||||
from langgraph.cache.redis import RedisCache
|
||||
|
||||
|
||||
class TestRedisCache:
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
def setup(self) -> None:
|
||||
"""Set up test Redis client and cache."""
|
||||
self.client = redis.Redis(
|
||||
host="localhost", port=6379, db=0, decode_responses=False
|
||||
@@ -20,21 +21,21 @@ class TestRedisCache:
|
||||
except redis.ConnectionError:
|
||||
pytest.skip("Redis server not available")
|
||||
|
||||
self.cache = RedisCache(self.client, prefix="test:cache:")
|
||||
self.cache: RedisCache = RedisCache(self.client, prefix="test:cache:")
|
||||
|
||||
# Clean up before each test
|
||||
self.client.flushdb()
|
||||
|
||||
def teardown_method(self):
|
||||
def teardown_method(self) -> None:
|
||||
"""Clean up after each test."""
|
||||
try:
|
||||
self.client.flushdb()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_basic_set_and_get(self):
|
||||
def test_basic_set_and_get(self) -> None:
|
||||
"""Test basic set and get operations."""
|
||||
keys = [(("graph", "node"), "key1")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "key1")]
|
||||
values = {keys[0]: ({"result": 42}, None)}
|
||||
|
||||
# Set value
|
||||
@@ -45,9 +46,9 @@ class TestRedisCache:
|
||||
assert len(result) == 1
|
||||
assert result[keys[0]] == {"result": 42}
|
||||
|
||||
def test_batch_operations(self):
|
||||
def test_batch_operations(self) -> None:
|
||||
"""Test batch set and get operations."""
|
||||
keys = [
|
||||
keys: list[FullKey] = [
|
||||
(("graph", "node1"), "key1"),
|
||||
(("graph", "node2"), "key2"),
|
||||
(("other", "node"), "key3"),
|
||||
@@ -68,9 +69,9 @@ class TestRedisCache:
|
||||
assert result[keys[1]] == {"result": 2}
|
||||
assert result[keys[2]] == {"result": 3}
|
||||
|
||||
def test_ttl_behavior(self):
|
||||
def test_ttl_behavior(self) -> None:
|
||||
"""Test TTL (time-to-live) functionality."""
|
||||
key = (("graph", "node"), "ttl_key")
|
||||
key: FullKey = (("graph", "node"), "ttl_key")
|
||||
values = {key: ({"data": "expires_soon"}, 1)} # 1 second TTL
|
||||
|
||||
# Set with TTL
|
||||
@@ -88,10 +89,10 @@ class TestRedisCache:
|
||||
result = self.cache.get([key])
|
||||
assert len(result) == 0
|
||||
|
||||
def test_namespace_isolation(self):
|
||||
def test_namespace_isolation(self) -> None:
|
||||
"""Test that different namespaces are isolated."""
|
||||
key1 = (("graph1", "node"), "same_key")
|
||||
key2 = (("graph2", "node"), "same_key")
|
||||
key1: FullKey = (("graph1", "node"), "same_key")
|
||||
key2: FullKey = (("graph2", "node"), "same_key")
|
||||
|
||||
values = {key1: ({"graph": 1}, None), key2: ({"graph": 2}, None)}
|
||||
|
||||
@@ -101,9 +102,12 @@ class TestRedisCache:
|
||||
assert result[key1] == {"graph": 1}
|
||||
assert result[key2] == {"graph": 2}
|
||||
|
||||
def test_clear_all(self):
|
||||
def test_clear_all(self) -> None:
|
||||
"""Test clearing all cached values."""
|
||||
keys = [(("graph", "node1"), "key1"), (("graph", "node2"), "key2")]
|
||||
keys: list[FullKey] = [
|
||||
(("graph", "node1"), "key1"),
|
||||
(("graph", "node2"), "key2"),
|
||||
]
|
||||
values = {keys[0]: ({"result": 1}, None), keys[1]: ({"result": 2}, None)}
|
||||
|
||||
self.cache.set(values)
|
||||
@@ -119,9 +123,9 @@ class TestRedisCache:
|
||||
result = self.cache.get(keys)
|
||||
assert len(result) == 0
|
||||
|
||||
def test_clear_by_namespace(self):
|
||||
def test_clear_by_namespace(self) -> None:
|
||||
"""Test clearing cached values by namespace."""
|
||||
keys = [
|
||||
keys: list[FullKey] = [
|
||||
(("graph1", "node"), "key1"),
|
||||
(("graph2", "node"), "key2"),
|
||||
(("graph1", "other"), "key3"),
|
||||
@@ -142,7 +146,7 @@ class TestRedisCache:
|
||||
assert len(result) == 1
|
||||
assert result[keys[1]] == {"result": 2}
|
||||
|
||||
def test_empty_operations(self):
|
||||
def test_empty_operations(self) -> None:
|
||||
"""Test behavior with empty keys/values."""
|
||||
# Empty get
|
||||
result = self.cache.get([])
|
||||
@@ -151,14 +155,14 @@ class TestRedisCache:
|
||||
# Empty set
|
||||
self.cache.set({}) # Should not raise error
|
||||
|
||||
def test_nonexistent_keys(self):
|
||||
def test_nonexistent_keys(self) -> None:
|
||||
"""Test getting keys that don't exist."""
|
||||
keys = [(("graph", "node"), "nonexistent")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "nonexistent")]
|
||||
result = self.cache.get(keys)
|
||||
assert len(result) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_operations(self):
|
||||
async def test_async_operations(self) -> None:
|
||||
"""Test async set and get operations with sync Redis client."""
|
||||
# Create sync Redis client and cache (like main integration tests)
|
||||
client = redis.Redis(host="localhost", port=6379, db=1, decode_responses=False)
|
||||
@@ -167,9 +171,9 @@ class TestRedisCache:
|
||||
except Exception:
|
||||
pytest.skip("Redis not available")
|
||||
|
||||
cache = RedisCache(client, prefix="test:async:")
|
||||
cache: RedisCache = RedisCache(client, prefix="test:async:")
|
||||
|
||||
keys = [(("graph", "node"), "async_key")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "async_key")]
|
||||
values = {keys[0]: ({"async": True}, None)}
|
||||
|
||||
# Async set (delegates to sync)
|
||||
@@ -184,7 +188,7 @@ class TestRedisCache:
|
||||
client.flushdb()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_clear(self):
|
||||
async def test_async_clear(self) -> None:
|
||||
"""Test async clear operations with sync Redis client."""
|
||||
# Create sync Redis client and cache (like main integration tests)
|
||||
client = redis.Redis(host="localhost", port=6379, db=1, decode_responses=False)
|
||||
@@ -193,9 +197,9 @@ class TestRedisCache:
|
||||
except Exception:
|
||||
pytest.skip("Redis not available")
|
||||
|
||||
cache = RedisCache(client, prefix="test:async:")
|
||||
cache: RedisCache = RedisCache(client, prefix="test:async:")
|
||||
|
||||
keys = [(("graph", "node"), "key")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "key")]
|
||||
values = {keys[0]: ({"data": "test"}, None)}
|
||||
|
||||
await cache.aset(values)
|
||||
@@ -214,44 +218,44 @@ class TestRedisCache:
|
||||
# Cleanup
|
||||
client.flushdb()
|
||||
|
||||
def test_redis_unavailable_get(self):
|
||||
def test_redis_unavailable_get(self) -> None:
|
||||
"""Test behavior when Redis is unavailable during get operations."""
|
||||
# Create cache with non-existent Redis server
|
||||
bad_client = redis.Redis(
|
||||
host="nonexistent", port=9999, socket_connect_timeout=0.1
|
||||
)
|
||||
cache = RedisCache(bad_client, prefix="test:cache:")
|
||||
cache: RedisCache = RedisCache(bad_client, prefix="test:cache:")
|
||||
|
||||
keys = [(("graph", "node"), "key")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "key")]
|
||||
result = cache.get(keys)
|
||||
|
||||
# Should return empty dict when Redis unavailable
|
||||
assert result == {}
|
||||
|
||||
def test_redis_unavailable_set(self):
|
||||
def test_redis_unavailable_set(self) -> None:
|
||||
"""Test behavior when Redis is unavailable during set operations."""
|
||||
# Create cache with non-existent Redis server
|
||||
bad_client = redis.Redis(
|
||||
host="nonexistent", port=9999, socket_connect_timeout=0.1
|
||||
)
|
||||
cache = RedisCache(bad_client, prefix="test:cache:")
|
||||
cache: RedisCache = RedisCache(bad_client, prefix="test:cache:")
|
||||
|
||||
keys = [(("graph", "node"), "key")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "key")]
|
||||
values = {keys[0]: ({"data": "test"}, None)}
|
||||
|
||||
# Should not raise exception when Redis unavailable
|
||||
cache.set(values) # Should silently fail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_redis_unavailable_async(self):
|
||||
async def test_redis_unavailable_async(self) -> None:
|
||||
"""Test async behavior when Redis is unavailable."""
|
||||
# Create sync cache with non-existent Redis server (like main integration tests)
|
||||
bad_client = redis.Redis(
|
||||
host="nonexistent", port=9999, socket_connect_timeout=0.1
|
||||
)
|
||||
cache = RedisCache(bad_client, prefix="test:cache:")
|
||||
cache: RedisCache = RedisCache(bad_client, prefix="test:cache:")
|
||||
|
||||
keys = [(("graph", "node"), "key")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "key")]
|
||||
values = {keys[0]: ({"data": "test"}, None)}
|
||||
|
||||
# Should return empty dict for get (delegates to sync)
|
||||
@@ -261,10 +265,10 @@ class TestRedisCache:
|
||||
# Should not raise exception for set (delegates to sync)
|
||||
await cache.aset(values) # Should silently fail
|
||||
|
||||
def test_corrupted_data_handling(self):
|
||||
def test_corrupted_data_handling(self) -> None:
|
||||
"""Test handling of corrupted data in Redis."""
|
||||
# Set some valid data first
|
||||
keys = [(("graph", "node"), "valid_key")]
|
||||
keys: list[FullKey] = [(("graph", "node"), "valid_key")]
|
||||
values = {keys[0]: ({"data": "valid"}, None)}
|
||||
self.cache.set(values)
|
||||
|
||||
@@ -273,33 +277,36 @@ class TestRedisCache:
|
||||
self.client.set(corrupted_key, b"invalid:data:format:too:many:colons")
|
||||
|
||||
# Should skip corrupted entry and return only valid ones
|
||||
all_keys = [keys[0], (("graph", "node"), "corrupted_key")]
|
||||
all_keys: list[FullKey] = [keys[0], (("graph", "node"), "corrupted_key")]
|
||||
result = self.cache.get(all_keys)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[keys[0]] == {"data": "valid"}
|
||||
|
||||
def test_key_parsing_edge_cases(self):
|
||||
def test_key_parsing_edge_cases(self) -> None:
|
||||
"""Test key parsing with edge cases."""
|
||||
# Test empty namespace
|
||||
key1 = ((), "empty_ns")
|
||||
key1: FullKey = ((), "empty_ns")
|
||||
values = {key1: ({"data": "empty_ns"}, None)}
|
||||
self.cache.set(values)
|
||||
result = self.cache.get([key1])
|
||||
assert result[key1] == {"data": "empty_ns"}
|
||||
|
||||
# Test namespace with special characters
|
||||
key2 = (("graph:with:colons", "node-with-dashes"), "key_with_underscores")
|
||||
key2: FullKey = (
|
||||
("graph:with:colons", "node-with-dashes"),
|
||||
"key_with_underscores",
|
||||
)
|
||||
values = {key2: ({"data": "special_chars"}, None)}
|
||||
self.cache.set(values)
|
||||
result = self.cache.get([key2])
|
||||
assert result[key2] == {"data": "special_chars"}
|
||||
|
||||
def test_large_data_serialization(self):
|
||||
def test_large_data_serialization(self) -> None:
|
||||
"""Test handling of large data objects."""
|
||||
# Create a large data structure
|
||||
large_data = {"large_list": list(range(1000)), "nested": {"data": "x" * 1000}}
|
||||
key = (("graph", "node"), "large_key")
|
||||
key: FullKey = (("graph", "node"), "large_key")
|
||||
values = {key: (large_data, None)}
|
||||
|
||||
self.cache.set(values)
|
||||
|
||||
@@ -1021,3 +1021,27 @@ async def test_embed_with_path(fake_embeddings: CharacterEmbeddings) -> None:
|
||||
assert len(results) == 3
|
||||
doc5_result = next(r for r in results if r.key == "doc5")
|
||||
assert doc5_result.score is None
|
||||
|
||||
|
||||
def test_non_ascii(fake_embeddings: CharacterEmbeddings) -> None:
|
||||
"""Test support for non-ascii characters"""
|
||||
store = InMemoryStore(
|
||||
index={"dims": fake_embeddings.dims, "embed": fake_embeddings}
|
||||
)
|
||||
store.put(("user_123", "memories"), "1", {"text": "这是中文"}) # Chinese
|
||||
store.put(("user_123", "memories"), "2", {"text": "これは日本語です"}) # Japanese
|
||||
store.put(("user_123", "memories"), "3", {"text": "이건 한국어야"}) # Korean
|
||||
store.put(("user_123", "memories"), "4", {"text": "Это русский"}) # Russian
|
||||
store.put(("user_123", "memories"), "5", {"text": "यह रूसी है"}) # Hindi
|
||||
|
||||
result1 = store.search(("user_123", "memories"), query="这是中文")
|
||||
result2 = store.search(("user_123", "memories"), query="これは日本語です")
|
||||
result3 = store.search(("user_123", "memories"), query="이건 한국어야")
|
||||
result4 = store.search(("user_123", "memories"), query="Это русский")
|
||||
result5 = store.search(("user_123", "memories"), query="यह रूसी है")
|
||||
|
||||
assert result1[0].key == "1"
|
||||
assert result2[0].key == "2"
|
||||
assert result3[0].key == "3"
|
||||
assert result4[0].key == "4"
|
||||
assert result5[0].key == "5"
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
from collections.abc import Sequence
|
||||
from typing import Annotated, Literal, TypedDict
|
||||
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, StateGraph, add_messages
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
tools = [TavilySearchResults(max_results=1)]
|
||||
|
||||
model_anth = init_chat_model("claude-3-7-sonnet-20250219", model_provider="anthropic")
|
||||
model_oai = ChatOpenAI(temperature=0)
|
||||
|
||||
model_anth = model_anth.bind_tools(tools)
|
||||
model_oai = model_oai.bind_tools(tools)
|
||||
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
|
||||
|
||||
# Define the function that determines whether to continue or not
|
||||
def should_continue(state):
|
||||
messages = state["messages"]
|
||||
last_message = messages[-1]
|
||||
# If there are no tool calls, then we finish
|
||||
if not last_message.tool_calls:
|
||||
return "end"
|
||||
# Otherwise if there is, we continue
|
||||
else:
|
||||
return "continue"
|
||||
|
||||
|
||||
# Define the function that calls the model
|
||||
def call_model(state, config):
|
||||
if config["configurable"].get("model", "anthropic") == "anthropic":
|
||||
model = model_anth
|
||||
else:
|
||||
model = model_oai
|
||||
messages = state["messages"]
|
||||
response = model.invoke(messages)
|
||||
# We return a list, because this will get added to the existing list
|
||||
return {"messages": [response]}
|
||||
|
||||
|
||||
# Define the function to execute tools
|
||||
tool_node = ToolNode(tools)
|
||||
|
||||
|
||||
class ContextSchema(TypedDict):
|
||||
model: Literal["anthropic", "openai"]
|
||||
|
||||
|
||||
# Define a new graph
|
||||
workflow = StateGraph(AgentState, context_schema=ContextSchema)
|
||||
|
||||
# Define the two nodes we will cycle between
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("action", tool_node)
|
||||
|
||||
# Set the entrypoint as `agent`
|
||||
# This means that this node is the first one called
|
||||
workflow.set_entry_point("agent")
|
||||
|
||||
# We now add a conditional edge
|
||||
workflow.add_conditional_edges(
|
||||
# First, we define the start node. We use `agent`.
|
||||
# This means these are the edges taken after the `agent` node is called.
|
||||
"agent",
|
||||
# Next, we pass in the function that will determine which node is called next.
|
||||
should_continue,
|
||||
# Finally we pass in a mapping.
|
||||
# The keys are strings, and the values are other nodes.
|
||||
# END is a special node marking that the graph should finish.
|
||||
# What will happen is we will call `should_continue`, and then the output of that
|
||||
# will be matched against the keys in this mapping.
|
||||
# Based on which one it matches, that node will then be called.
|
||||
{
|
||||
# If `tools`, then we call the tool node.
|
||||
"continue": "action",
|
||||
# Otherwise we finish.
|
||||
"end": END,
|
||||
},
|
||||
)
|
||||
|
||||
# We now add a normal edge from `tools` to `agent`.
|
||||
# This means that after `tools` is called, `agent` node is called next.
|
||||
workflow.add_edge("action", "agent")
|
||||
|
||||
# Finally, we compile it!
|
||||
# This compiles it into a LangChain Runnable,
|
||||
# meaning you can use it as you would any other runnable
|
||||
graph = workflow.compile()
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"python_version": "3.12",
|
||||
"dependencies": [
|
||||
"."
|
||||
],
|
||||
"graphs": {
|
||||
"agent": "./agent.py:graph"
|
||||
},
|
||||
"env": "../.env"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
requests
|
||||
langchain_anthropic
|
||||
langchain_openai
|
||||
langchain_community
|
||||
langchain
|
||||
langgraph==1.0.0a2
|
||||
@@ -7,6 +7,7 @@ from langchain_core.messages import BaseMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, StateGraph, add_messages
|
||||
from langgraph.prebuilt import ToolNode
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
tools = [TavilySearchResults(max_results=1)]
|
||||
|
||||
@@ -17,6 +18,10 @@ model_anth = model_anth.bind_tools(tools)
|
||||
model_oai = model_oai.bind_tools(tools)
|
||||
|
||||
|
||||
class AgentContext(TypedDict):
|
||||
model: Literal["anthropic", "openai"]
|
||||
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
|
||||
@@ -34,8 +39,8 @@ def should_continue(state):
|
||||
|
||||
|
||||
# Define the function that calls the model
|
||||
def call_model(state, config):
|
||||
if config["configurable"].get("model", "anthropic") == "anthropic":
|
||||
def call_model(state, runtime: Runtime[AgentContext]):
|
||||
if runtime.context.get("model", "anthropic") == "anthropic":
|
||||
model = model_anth
|
||||
else:
|
||||
model = model_oai
|
||||
@@ -49,12 +54,8 @@ def call_model(state, config):
|
||||
tool_node = ToolNode(tools)
|
||||
|
||||
|
||||
class ContextSchema(TypedDict):
|
||||
model: Literal["anthropic", "openai"]
|
||||
|
||||
|
||||
# Define a new graph
|
||||
workflow = StateGraph(AgentState, context_schema=ContextSchema)
|
||||
workflow = StateGraph(AgentState, context_schema=AgentContext)
|
||||
|
||||
# Define the two nodes we will cycle between
|
||||
workflow.add_node("agent", call_model)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Annotated, TypedDict
|
||||
from typing import Annotated, Literal, TypedDict
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
@@ -8,6 +8,7 @@ from langchain_core.messages import BaseMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, StateGraph, add_messages
|
||||
from langgraph.prebuilt import ToolNode
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
tools = [TavilySearchResults(max_results=1)]
|
||||
|
||||
@@ -21,6 +22,10 @@ prompt = open(Path(__file__).parent.parent / "prompt.txt").read()
|
||||
subprompt = open(Path(__file__).parent / "subprompt.txt").read()
|
||||
|
||||
|
||||
class AgentContext(TypedDict):
|
||||
model: Literal["anthropic", "openai"]
|
||||
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
|
||||
@@ -38,8 +43,8 @@ def should_continue(state):
|
||||
|
||||
|
||||
# Define the function that calls the model
|
||||
def call_model(state, config):
|
||||
if config["configurable"].get("model", "anthropic") == "anthropic":
|
||||
def call_model(state, runtime: Runtime[AgentContext]):
|
||||
if runtime.context.get("model", "anthropic") == "anthropic":
|
||||
model = model_anth
|
||||
else:
|
||||
model = model_oai
|
||||
@@ -52,9 +57,8 @@ def call_model(state, config):
|
||||
# Define the function to execute tools
|
||||
tool_node = ToolNode(tools)
|
||||
|
||||
|
||||
# Define a new graph
|
||||
workflow = StateGraph(AgentState)
|
||||
workflow = StateGraph(AgentState, context_schema=AgentContext)
|
||||
|
||||
# Define the two nodes we will cycle between
|
||||
workflow.add_node("agent", call_model)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Annotated, TypedDict
|
||||
from typing import Annotated, Literal, TypedDict
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
@@ -8,6 +8,7 @@ from langchain_core.messages import BaseMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, StateGraph, add_messages
|
||||
from langgraph.prebuilt import ToolNode
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
tools = [TavilySearchResults(max_results=1)]
|
||||
|
||||
@@ -21,6 +22,10 @@ prompt = open(Path(__file__).parent.parent / "prompt.txt").read()
|
||||
subprompt = open(Path(__file__).parent / "subprompt.txt").read()
|
||||
|
||||
|
||||
class AgentContext(TypedDict):
|
||||
model: Literal["anthropic", "openai"]
|
||||
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
|
||||
@@ -38,8 +43,8 @@ def should_continue(state):
|
||||
|
||||
|
||||
# Define the function that calls the model
|
||||
def call_model(state, config):
|
||||
if config["configurable"].get("model", "anthropic") == "anthropic":
|
||||
def call_model(state, runtime: Runtime[AgentContext]):
|
||||
if runtime.context.get("model", "anthropic") == "anthropic":
|
||||
model = model_anth
|
||||
else:
|
||||
model = model_oai
|
||||
@@ -54,7 +59,7 @@ tool_node = ToolNode(tools)
|
||||
|
||||
|
||||
# Define a new graph
|
||||
workflow = StateGraph(AgentState)
|
||||
workflow = StateGraph(AgentState, context_schema=AgentContext)
|
||||
|
||||
# Define the two nodes we will cycle between
|
||||
workflow.add_node("agent", call_model)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"prettier",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
parser: "@typescript-eslint/parser",
|
||||
project: "./tsconfig.json",
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["import", "@typescript-eslint", "no-instanceof"],
|
||||
ignorePatterns: [
|
||||
".eslintrc.cjs",
|
||||
"scripts",
|
||||
"src/utils/lodash/*",
|
||||
"node_modules",
|
||||
"dist",
|
||||
"dist-cjs",
|
||||
"*.js",
|
||||
"*.cjs",
|
||||
"*.d.ts",
|
||||
],
|
||||
rules: {
|
||||
"no-process-env": 2,
|
||||
"no-instanceof/no-instanceof": 2,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-shadow": 0,
|
||||
"@typescript-eslint/no-empty-interface": 0,
|
||||
"@typescript-eslint/no-use-before-define": ["error", "nofunc"],
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { args: "none" }],
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
camelcase: 0,
|
||||
"class-methods-use-this": 0,
|
||||
"import/extensions": [2, "ignorePackages"],
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{ devDependencies: ["**/*.test.ts"] },
|
||||
],
|
||||
"import/no-unresolved": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"keyword-spacing": "error",
|
||||
"max-classes-per-file": 0,
|
||||
"max-len": 0,
|
||||
"no-await-in-loop": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-console": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"no-shadow": 0,
|
||||
"no-continue": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-useless-constructor": 0,
|
||||
"no-return-await": 0,
|
||||
"consistent-return": 0,
|
||||
"no-else-return": 0,
|
||||
"new-cap": ["error", { properties: false, capIsNew: false }],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": {
|
||||
"agent": "./src/graph.ts:graph"
|
||||
},
|
||||
"env": "../../.env"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@js-monorepo-example/agent",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "src/graph.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@js-monorepo-example/shared": "*",
|
||||
"@langchain/core": "^0.3.2",
|
||||
"@langchain/langgraph": "^0.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Simple LangGraph.js example for monorepo testing
|
||||
*/
|
||||
import { StateGraph } from "@langchain/langgraph";
|
||||
import { RunnableConfig } from "@langchain/core/runnables";
|
||||
import { StateAnnotation } from "./state.js";
|
||||
import { getGreeting } from "@js-monorepo-example/shared";
|
||||
|
||||
/**
|
||||
* Simple node that uses the shared library
|
||||
*/
|
||||
const callModel = async (
|
||||
state: typeof StateAnnotation.State,
|
||||
_config: RunnableConfig,
|
||||
): Promise<typeof StateAnnotation.Update> => {
|
||||
// Use functions from the shared library
|
||||
const greeting = getGreeting();
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: `${greeting}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple routing function
|
||||
*/
|
||||
export const route = (
|
||||
state: typeof StateAnnotation.State,
|
||||
): "__end__" | "callModel" => {
|
||||
if (state.messages.length > 0) {
|
||||
return "__end__";
|
||||
}
|
||||
return "callModel";
|
||||
};
|
||||
|
||||
// Create the graph
|
||||
const builder = new StateGraph(StateAnnotation)
|
||||
.addNode("callModel", callModel)
|
||||
.addEdge("__start__", "callModel")
|
||||
.addConditionalEdges("callModel", route);
|
||||
|
||||
export const graph = builder.compile();
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
|
||||
import { Annotation, messagesStateReducer } from "@langchain/langgraph";
|
||||
|
||||
/**
|
||||
* Simple state annotation for the agent
|
||||
*/
|
||||
export const StateAnnotation = Annotation.Root({
|
||||
/**
|
||||
* Messages track the primary execution state of the agent.
|
||||
*/
|
||||
messages: Annotation<BaseMessage[], BaseMessageLike[]>({
|
||||
reducer: messagesStateReducer,
|
||||
default: () => [],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@js-monorepo-example/shared",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Simple utility functions for monorepo testing
|
||||
*/
|
||||
export function getGreeting(): string {
|
||||
return "Hello from shared library!";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "js-monorepo-example",
|
||||
"version": "0.0.1",
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"description": "A simple monorepo example for LangGraph integration testing.",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"libs/*",
|
||||
"apps/*"
|
||||
],
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "turbo build",
|
||||
"clean": "turbo clean",
|
||||
"test": "turbo test",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint 'apps/**/*.ts' 'libs/**/*.ts'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"turbo": "^2.5.0",
|
||||
"typescript": "^5.3.3",
|
||||
"@tsconfig/recommended": "^1.0.7",
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "^9.9.1",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-no-instanceof": "^1.0.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||
"@typescript-eslint/parser": "^5.59.8",
|
||||
"prettier": "^3.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@tsconfig/recommended",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["apps/**/*", "libs/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"clean": {
|
||||
"dependsOn": ["^clean"]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["^test"]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
__version__ = "0.3.8"
|
||||
__version__ = "0.4.2"
|
||||
|
||||
@@ -303,6 +303,8 @@ def _build(
|
||||
pull: bool,
|
||||
tag: str,
|
||||
passthrough: Sequence[str] = (),
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
):
|
||||
# pull latest images
|
||||
if pull:
|
||||
@@ -322,22 +324,38 @@ def _build(
|
||||
"-t",
|
||||
tag,
|
||||
]
|
||||
# determine build context: use current directory for JS projects, config parent for Python
|
||||
is_js_project = config_json.get("node_version") and not config_json.get(
|
||||
"python_version"
|
||||
)
|
||||
# build/install commands only apply to JS projects for now
|
||||
# without install/build command, JS projects will follow the old behavior
|
||||
if is_js_project and (build_command or install_command):
|
||||
build_context = str(pathlib.Path.cwd())
|
||||
else:
|
||||
build_context = str(config.parent)
|
||||
|
||||
# apply config
|
||||
stdin, additional_contexts = langgraph_cli.config.config_to_docker(
|
||||
config, config_json, base_image, api_version
|
||||
config,
|
||||
config_json,
|
||||
base_image,
|
||||
api_version,
|
||||
install_command,
|
||||
build_command,
|
||||
build_context,
|
||||
)
|
||||
# add additional_contexts
|
||||
if additional_contexts:
|
||||
for k, v in additional_contexts.items():
|
||||
args.extend(["--build-context", f"{k}={v}"])
|
||||
# run docker build
|
||||
runner.run(
|
||||
subp_exec(
|
||||
"docker",
|
||||
"build",
|
||||
*args,
|
||||
*passthrough,
|
||||
str(config.parent),
|
||||
build_context,
|
||||
input=stdin,
|
||||
verbose=True,
|
||||
)
|
||||
@@ -366,6 +384,14 @@ def _build(
|
||||
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
|
||||
)
|
||||
@OPT_API_VERSION
|
||||
@click.option(
|
||||
"--install-command",
|
||||
help="Custom install command to run from the build context root. If not provided, auto-detects based on package manager files.",
|
||||
)
|
||||
@click.option(
|
||||
"--build-command",
|
||||
help="Custom build command to run from the langgraph.json directory. If not provided, uses default build process.",
|
||||
)
|
||||
@click.argument("docker_build_args", nargs=-1, type=click.UNPROCESSED)
|
||||
@cli.command(
|
||||
help="📦 Build LangGraph API server Docker image.",
|
||||
@@ -381,6 +407,8 @@ def build(
|
||||
api_version: Optional[str],
|
||||
pull: bool,
|
||||
tag: str,
|
||||
install_command: Optional[str],
|
||||
build_command: Optional[str],
|
||||
):
|
||||
with Runner() as runner, Progress(message="Pulling...") as set:
|
||||
if shutil.which("docker") is None:
|
||||
@@ -397,6 +425,8 @@ def build(
|
||||
pull,
|
||||
tag,
|
||||
docker_build_args,
|
||||
install_command,
|
||||
build_command,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -357,6 +357,8 @@ class HttpConfig(TypedDict, total=False):
|
||||
|
||||
You can include or exclude headers as configurable values to condition your
|
||||
agent's behavior or permissions on a request's headers."""
|
||||
logging_headers: Optional[ConfigurableHeaderConfig]
|
||||
"""Optional. Defines which headers are excluded from logging."""
|
||||
|
||||
|
||||
class Config(TypedDict, total=False):
|
||||
@@ -911,10 +913,10 @@ def _assemble_local_deps(config_path: pathlib.Path, config: Config) -> LocalDeps
|
||||
"Rename the directory to use it as flat-layout package."
|
||||
)
|
||||
check_reserved(resolved.name, local_dep)
|
||||
container_path = f"/deps/__outer_{resolved.name}/{resolved.name}"
|
||||
container_path = f"/deps/outer-{resolved.name}/{resolved.name}"
|
||||
else:
|
||||
# src layout
|
||||
container_path = f"/deps/__outer_{resolved.name}/src"
|
||||
container_path = f"/deps/outer-{resolved.name}/src"
|
||||
for file in files:
|
||||
rfile = resolved / file
|
||||
if (
|
||||
@@ -1254,7 +1256,7 @@ def python_config_to_docker(
|
||||
else:
|
||||
pip_installer = "pip"
|
||||
if pip_installer == "uv":
|
||||
install_cmd = "uv pip install --system"
|
||||
install_cmd = "uv pip install --system --prerelease=allow"
|
||||
elif pip_installer == "pip":
|
||||
install_cmd = "pip install"
|
||||
else:
|
||||
@@ -1284,7 +1286,7 @@ def python_config_to_docker(
|
||||
if local_deps.pip_reqs:
|
||||
pip_reqs_str = os.linesep.join(
|
||||
(
|
||||
f"COPY --from=__outer_{reqpath.name} requirements.txt {destpath}"
|
||||
f"COPY --from=outer-{reqpath.name} requirements.txt {destpath}"
|
||||
if reqpath.parent in local_deps.additional_contexts
|
||||
else f"ADD {reqpath.relative_to(config_path.parent)} {destpath}"
|
||||
)
|
||||
@@ -1303,7 +1305,7 @@ def python_config_to_docker(
|
||||
faux_pkgs_str = f"{os.linesep}{os.linesep}".join(
|
||||
(
|
||||
f"""# -- Adding non-package dependency {fullpath.name} --
|
||||
COPY --from=__outer_{fullpath.name} . {destpath}"""
|
||||
COPY --from=outer-{fullpath.name} . {destpath}"""
|
||||
if fullpath in local_deps.additional_contexts
|
||||
else f"""# -- Adding non-package dependency {fullpath.name} --
|
||||
ADD {relpath} {destpath}"""
|
||||
@@ -1318,7 +1320,7 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_{fullpath.name}/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-{fullpath.name}/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency {fullpath.name} --"""
|
||||
for fullpath, (relpath, destpath) in local_deps.faux_pkgs.items()
|
||||
@@ -1421,7 +1423,7 @@ ADD {relpath} /deps/{name}
|
||||
if p in local_deps.real_pkgs:
|
||||
name = local_deps.real_pkgs[p][1]
|
||||
elif p in local_deps.faux_pkgs:
|
||||
name = f"__outer_{p.name}"
|
||||
name = f"outer-{p.name}"
|
||||
else:
|
||||
raise RuntimeError(f"Unknown additional context: {p}")
|
||||
additional_contexts[name] = str(p)
|
||||
@@ -1434,9 +1436,28 @@ def node_config_to_docker(
|
||||
config: Config,
|
||||
base_image: str,
|
||||
api_version: Optional[str] = None,
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
build_context: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
faux_path = f"/deps/{config_path.parent.name}"
|
||||
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
||||
# Calculate paths for monorepo support
|
||||
if build_context:
|
||||
relative_workdir = _calculate_relative_workdir(config_path, build_context)
|
||||
container_name = pathlib.Path(build_context).name
|
||||
if relative_workdir:
|
||||
faux_path = f"/deps/{container_name}/{relative_workdir}"
|
||||
else:
|
||||
faux_path = f"/deps/{container_name}"
|
||||
else:
|
||||
# Backward compatibility: use the original behavior
|
||||
faux_path = f"/deps/{config_path.parent.name}"
|
||||
|
||||
# Use custom install command or auto-detect
|
||||
if install_command:
|
||||
install_cmd = install_command
|
||||
else:
|
||||
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
||||
|
||||
image_str = docker_tag(config, base_image, api_version)
|
||||
|
||||
env_vars: list[str] = []
|
||||
@@ -1463,20 +1484,35 @@ def node_config_to_docker(
|
||||
|
||||
env_vars.append(f"ENV LANGSERVE_GRAPHS='{json.dumps(config['graphs'])}'")
|
||||
|
||||
# For monorepo support, we need to handle install and build commands differently
|
||||
if build_context:
|
||||
# Monorepo case: install from root, build from config directory
|
||||
container_root = f"/deps/{pathlib.Path(build_context).name}"
|
||||
install_step = f"RUN cd {container_root} && {install_cmd}"
|
||||
|
||||
if build_command:
|
||||
build_step = f"RUN cd {faux_path} && {build_command}"
|
||||
else:
|
||||
build_step = 'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts'
|
||||
else:
|
||||
# Original behavior: everything happens in the same directory
|
||||
install_step = f"RUN cd {faux_path} && {install_cmd}"
|
||||
build_step = 'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts'
|
||||
|
||||
docker_file_contents = [
|
||||
f"FROM {image_str}",
|
||||
"",
|
||||
os.linesep.join(config["dockerfile_lines"]),
|
||||
"",
|
||||
f"ADD . {faux_path}",
|
||||
f"ADD . {faux_path if not build_context else container_root}",
|
||||
"",
|
||||
f"RUN cd {faux_path} && {install_cmd}",
|
||||
install_step,
|
||||
"",
|
||||
os.linesep.join(env_vars),
|
||||
"",
|
||||
f"WORKDIR {faux_path}",
|
||||
"",
|
||||
'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts',
|
||||
build_step,
|
||||
]
|
||||
|
||||
return os.linesep.join(docker_file_contents), {}
|
||||
@@ -1524,16 +1560,42 @@ def docker_tag(
|
||||
return f"{base_image}:{full_tag}"
|
||||
|
||||
|
||||
def _calculate_relative_workdir(config_path: pathlib.Path, build_context: str) -> str:
|
||||
"""Calculate the relative path from build context to langgraph.json directory."""
|
||||
config_dir = config_path.parent.resolve()
|
||||
build_context_path = pathlib.Path(build_context).resolve()
|
||||
|
||||
try:
|
||||
relative_path = config_dir.relative_to(build_context_path)
|
||||
return str(relative_path) if str(relative_path) != "." else ""
|
||||
except ValueError as _:
|
||||
raise ValueError(
|
||||
f"Configuration file {config_path} is not under the build context {build_context}. "
|
||||
f"Please run the command from a directory that contains your langgraph.json file, "
|
||||
) from None
|
||||
|
||||
|
||||
def config_to_docker(
|
||||
config_path: pathlib.Path,
|
||||
config: Config,
|
||||
base_image: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
build_context: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
base_image = base_image or default_base_image(config)
|
||||
|
||||
if config.get("node_version") and not config.get("python_version"):
|
||||
return node_config_to_docker(config_path, config, base_image, api_version)
|
||||
return node_config_to_docker(
|
||||
config_path,
|
||||
config,
|
||||
base_image,
|
||||
api_version,
|
||||
install_command,
|
||||
build_command,
|
||||
build_context,
|
||||
)
|
||||
|
||||
return python_config_to_docker(config_path, config, base_image, api_version)
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ dependencies = [
|
||||
path = "langgraph_cli/__init__.py"
|
||||
[project.optional-dependencies]
|
||||
inmem = [
|
||||
"langgraph-api>=0.2.120,<0.4.0 ; python_version >= '3.11'",
|
||||
"langgraph-runtime-inmem>=0.6.8 ; python_version >= '3.11'",
|
||||
"langgraph-api>=0.3,<0.5.0 ; python_version >= '3.11'",
|
||||
"langgraph-runtime-inmem>=0.7 ; python_version >= '3.11'",
|
||||
"python-dotenv>=0.8.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": [".", "../../libs/shared", "../../libs/common"],
|
||||
"graphs": {
|
||||
"agent": "./src/agent/graph.py:graph"
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
[project]
|
||||
name = "agent"
|
||||
version = "0.0.1"
|
||||
description = "Agent for the Python monorepo"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["agent"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"agent" = "src/agent"
|
||||
@@ -0,0 +1 @@
|
||||
"""Agent package."""
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Simple LangGraph agent for monorepo testing."""
|
||||
|
||||
from common import get_common_prefix
|
||||
from langchain_core.messages import AIMessage
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from shared import get_dummy_message
|
||||
|
||||
from agent.state import State
|
||||
|
||||
|
||||
def call_model(state: State) -> dict:
|
||||
"""Simple node that uses the shared libraries."""
|
||||
# Use functions from both shared packages
|
||||
dummy_message = get_dummy_message()
|
||||
prefix = get_common_prefix()
|
||||
|
||||
message = AIMessage(content=f"{prefix} Agent says: {dummy_message}")
|
||||
|
||||
return {"messages": [message]}
|
||||
|
||||
|
||||
def should_continue(state: State):
|
||||
"""Conditional edge - end after first message."""
|
||||
messages = state["messages"]
|
||||
if len(messages) > 0:
|
||||
return END
|
||||
return "call_model"
|
||||
|
||||
|
||||
# Build the graph
|
||||
workflow = StateGraph(State)
|
||||
|
||||
# Add the node
|
||||
workflow.add_node("call_model", call_model)
|
||||
|
||||
# Add edges
|
||||
workflow.add_edge(START, "call_model")
|
||||
workflow.add_conditional_edges("call_model", should_continue)
|
||||
|
||||
graph = workflow.compile()
|
||||
@@ -0,0 +1,13 @@
|
||||
"""State definition for the agent."""
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Annotated, TypedDict
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langgraph.graph.message import add_messages
|
||||
|
||||
|
||||
class State(TypedDict):
|
||||
"""The state of the agent."""
|
||||
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Common helper functions package."""
|
||||
|
||||
from .helpers import get_common_prefix
|
||||
|
||||
__all__ = ["get_common_prefix"]
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Common helper functions."""
|
||||
|
||||
|
||||
def get_common_prefix() -> str:
|
||||
"""Get a common prefix for messages."""
|
||||
return "[COMMON]"
|
||||
@@ -0,0 +1,20 @@
|
||||
[project]
|
||||
name = "shared"
|
||||
version = "0.0.1"
|
||||
description = "Shared utilities for the Python monorepo"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
dependencies = []
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["shared"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"shared" = "src/shared"
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Shared utilities package."""
|
||||
|
||||
from .utils import get_dummy_message
|
||||
|
||||
__all__ = ["get_dummy_message"]
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Shared utility functions."""
|
||||
|
||||
|
||||
def get_dummy_message() -> str:
|
||||
"""Get a dummy message for testing."""
|
||||
return "Hello from shared library!"
|
||||
@@ -0,0 +1,46 @@
|
||||
[project]
|
||||
name = "python-monorepo-example"
|
||||
version = "0.0.1"
|
||||
description = "A Python monorepo example with LangGraph agents and shared packages"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
dependencies = [
|
||||
"langgraph>=0.6.0,<0.7.0",
|
||||
"langchain-core>=0.2.14",
|
||||
]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["apps/*", "libs/shared"]
|
||||
|
||||
[tool.uv.sources]
|
||||
shared = { workspace = true }
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["mypy>=1.11.1", "ruff>=0.6.1"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"D", # pydocstyle
|
||||
"UP",
|
||||
]
|
||||
lint.ignore = [
|
||||
"D100", # Missing docstring in public module
|
||||
"D101", # Missing docstring in public class
|
||||
"D102", # Missing docstring in public method
|
||||
"D103", # Missing docstring in public function
|
||||
"D104", # Missing docstring in public package
|
||||
"D105", # Missing docstring in magic method
|
||||
]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
@@ -576,6 +576,17 @@
|
||||
"disable_threads": {
|
||||
"type": "boolean",
|
||||
"description": "Optional. If True, /threads routes are removed.\n\nDefault is False.\n"
|
||||
},
|
||||
"logging_headers": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ConfigurableHeaderConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Defines which headers are excluded from logging."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
||||
@@ -576,6 +576,17 @@
|
||||
"disable_threads": {
|
||||
"type": "boolean",
|
||||
"description": "Optional. If True, /threads routes are removed.\n\nDefault is False.\n"
|
||||
},
|
||||
"logging_headers": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ConfigurableHeaderConfig"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Defines which headers are excluded from logging."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
||||
@@ -15,7 +15,7 @@ from langgraph_cli.docker import DEFAULT_POSTGRES_URI, DockerCapabilities, Versi
|
||||
from langgraph_cli.util import clean_empty_lines
|
||||
|
||||
FORMATTED_CLEANUP_LINES = _get_pip_cleanup_lines(
|
||||
install_cmd="uv pip install --system",
|
||||
install_cmd="uv pip install --system --prerelease=allow",
|
||||
to_uninstall=("pip", "setuptools", "wheel"),
|
||||
pip_installer="uv",
|
||||
)
|
||||
@@ -149,7 +149,7 @@ services:
|
||||
COPY --from=cli_1 . /deps/cli_1
|
||||
# -- End of local package ../../.. --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
@@ -570,7 +570,7 @@ def test_build_generate_proper_build_context():
|
||||
catch_exceptions=True,
|
||||
)
|
||||
|
||||
build_context_pattern = re.compile(r"--build-context\s+(\w+)=([^\s]+)")
|
||||
build_context_pattern = re.compile(r"--build-context\s+([\w-]+)=([^\s]+)")
|
||||
|
||||
build_contexts = re.findall(build_context_pattern, result.output)
|
||||
assert len(build_contexts) == 2, (
|
||||
|
||||
@@ -20,7 +20,7 @@ from langgraph_cli.config import (
|
||||
from langgraph_cli.util import clean_empty_lines
|
||||
|
||||
FORMATTED_CLEANUP_LINES = _get_pip_cleanup_lines(
|
||||
install_cmd="uv pip install --system",
|
||||
install_cmd="uv pip install --system --prerelease=allow",
|
||||
to_uninstall=("pip", "setuptools", "wheel"),
|
||||
pip_installer="uv",
|
||||
)
|
||||
@@ -421,14 +421,14 @@ def test_config_to_docker_simple():
|
||||
expected_docker_stdin = f"""\
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Installing local requirements --
|
||||
COPY --from=__outer_requirements.txt requirements.txt /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -r /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
COPY --from=outer-requirements.txt requirements.txt /deps/outer-graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -r /deps/outer-graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
# -- End of local requirements install --
|
||||
# -- Adding local package ../../examples --
|
||||
COPY --from=examples . /deps/examples
|
||||
# -- End of local package ../../examples --
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -438,11 +438,11 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Adding non-package dependency graphs_reqs_a --
|
||||
COPY --from=__outer_graphs_reqs_a . /deps/__outer_graphs_reqs_a/graphs_reqs_a
|
||||
COPY --from=outer-graphs_reqs_a . /deps/outer-graphs_reqs_a/graphs_reqs_a
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs_reqs_a"' \\
|
||||
@@ -452,21 +452,21 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs_reqs_a/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs_reqs_a/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs_reqs_a --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGGRAPH_HTTP='{{"app": "/deps/examples/my_app.py:app"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
|
||||
assert additional_contexts == {
|
||||
"__outer_graphs_reqs_a": str(
|
||||
"outer-graphs_reqs_a": str(
|
||||
(pathlib.Path(__file__).parent / "../../examples/graphs_reqs_a").resolve()
|
||||
),
|
||||
"examples": str((pathlib.Path(__file__).parent / "../../examples").resolve()),
|
||||
@@ -484,7 +484,7 @@ def test_config_to_docker_outside_path():
|
||||
"""\
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -494,11 +494,11 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Adding non-package dependency tests --
|
||||
COPY --from=__outer_tests . /deps/__outer_tests/tests
|
||||
COPY --from=outer-tests . /deps/outer-tests/tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "tests"' \\
|
||||
@@ -508,22 +508,22 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}'
|
||||
"""
|
||||
+ FORMATTED_CLEANUP_LINES
|
||||
+ """
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
)
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {
|
||||
"__outer_tests": str(pathlib.Path(__file__).parent.parent.absolute()),
|
||||
"outer-tests": str(pathlib.Path(__file__).parent.parent.absolute()),
|
||||
}
|
||||
|
||||
|
||||
@@ -545,7 +545,7 @@ def test_config_to_docker_pipconfig():
|
||||
FROM langchain/langgraph-api:3.11
|
||||
ADD pipconfig.txt /pipconfig.txt
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -555,17 +555,17 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}'
|
||||
"""
|
||||
+ FORMATTED_CLEANUP_LINES
|
||||
+ """
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
)
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
@@ -607,7 +607,7 @@ def test_config_to_docker_local_deps():
|
||||
expected_docker_stdin = f"""\
|
||||
FROM langchain/langgraph-api-custom:3.11
|
||||
# -- Adding non-package dependency graphs --
|
||||
ADD ./graphs /deps/__outer_graphs/src
|
||||
ADD ./graphs /deps/outer-graphs/src
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs"' \\
|
||||
@@ -617,13 +617,13 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_graphs/src/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}\
|
||||
"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
@@ -657,7 +657,7 @@ dependencies = ["langchain"]"""
|
||||
ADD . /deps/unit_tests
|
||||
# -- End of local package . --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/unit_tests/graphs/agent.py:graph"}'
|
||||
"""
|
||||
@@ -689,9 +689,9 @@ def test_config_to_docker_end_to_end():
|
||||
ARG meow
|
||||
ARG foo
|
||||
ADD pipconfig.txt /pipconfig.txt
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt langchain langchain_openai
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt langchain langchain_openai
|
||||
# -- Adding non-package dependency graphs --
|
||||
ADD ./graphs/ /deps/__outer_graphs/src
|
||||
ADD ./graphs/ /deps/outer-graphs/src
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs"' \\
|
||||
@@ -701,13 +701,13 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_graphs/src/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -797,7 +797,7 @@ def test_config_to_docker_gen_ui_python():
|
||||
expected_docker_stdin = f"""FROM langchain/langgraph-api:3.11
|
||||
RUN /storage/install-node.sh
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -807,21 +807,21 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGGRAPH_UI='{{"agent": "./graphs/agent.ui.jsx"}}'
|
||||
ENV LANGGRAPH_UI_CONFIG='{{"shared": ["nuqs"]}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
# -- Installing JS dependencies --
|
||||
ENV NODE_VERSION=20
|
||||
RUN cd /deps/__outer_unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
RUN cd /deps/outer-unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
# -- End of JS dependencies install --
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests"""
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests"""
|
||||
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -843,7 +843,7 @@ def test_config_to_docker_multiplatform():
|
||||
expected_docker_stdin = f"""FROM langchain/langgraph-api:3.11
|
||||
RUN /storage/install-node.sh
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -853,19 +853,19 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"python": "/deps/__outer_unit_tests/unit_tests/multiplatform/python.py:graph", "js": "/deps/__outer_unit_tests/unit_tests/multiplatform/js.mts:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"python": "/deps/outer-unit_tests/unit_tests/multiplatform/python.py:graph", "js": "/deps/outer-unit_tests/unit_tests/multiplatform/js.mts:graph"}}'
|
||||
# -- Installing JS dependencies --
|
||||
ENV NODE_VERSION=22
|
||||
RUN cd /deps/__outer_unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
RUN cd /deps/outer-unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
# -- End of JS dependencies install --
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests"""
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests"""
|
||||
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -887,7 +887,7 @@ def test_config_to_docker_pip_installer():
|
||||
docker_auto, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_auto, "langchain/langgraph-api:0.2.47"
|
||||
)
|
||||
assert "uv pip install --system" in docker_auto
|
||||
assert "uv pip install --system --prerelease=allow" in docker_auto
|
||||
assert "rm /usr/bin/uv /usr/bin/uvx" in docker_auto
|
||||
|
||||
# Test explicit pip setting
|
||||
@@ -895,7 +895,7 @@ def test_config_to_docker_pip_installer():
|
||||
docker_pip, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_pip, "langchain/langgraph-api:0.2.47"
|
||||
)
|
||||
assert "uv pip install --system" not in docker_pip
|
||||
assert "uv pip install --system --prerelease=allow" not in docker_pip
|
||||
assert "pip install" in docker_pip
|
||||
assert "rm /usr/bin/uv" not in docker_pip
|
||||
|
||||
@@ -904,7 +904,7 @@ def test_config_to_docker_pip_installer():
|
||||
docker_uv, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_uv, "langchain/langgraph-api:0.2.47"
|
||||
)
|
||||
assert "uv pip install --system" in docker_uv
|
||||
assert "uv pip install --system --prerelease=allow" in docker_uv
|
||||
assert "rm /usr/bin/uv /usr/bin/uvx" in docker_uv
|
||||
|
||||
# Test auto behavior with older image (should use pip)
|
||||
@@ -914,7 +914,7 @@ def test_config_to_docker_pip_installer():
|
||||
docker_auto_old, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_auto_old, "langchain/langgraph-api:0.2.46"
|
||||
)
|
||||
assert "uv pip install --system" not in docker_auto_old
|
||||
assert "uv pip install --system --prerelease=allow" not in docker_auto_old
|
||||
assert "pip install" in docker_auto_old
|
||||
assert "rm /usr/bin/uv" not in docker_auto_old
|
||||
|
||||
@@ -923,7 +923,7 @@ def test_config_to_docker_pip_installer():
|
||||
docker_default, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_default, "langchain/langgraph-api:0.2.47"
|
||||
)
|
||||
assert "uv pip install --system" in docker_default
|
||||
assert "uv pip install --system --prerelease=allow" in docker_default
|
||||
|
||||
|
||||
def test_config_retain_build_tools():
|
||||
@@ -984,7 +984,7 @@ def test_config_to_compose_simple_config():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -994,15 +994,15 @@ def test_config_to_compose_simple_config():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
actual_compose_stdin = config_to_compose(
|
||||
PATH_TO_CONFIG,
|
||||
@@ -1025,7 +1025,7 @@ def test_config_to_compose_env_vars():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api-custom:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1035,15 +1035,15 @@ def test_config_to_compose_env_vars():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
openai_api_key = "key"
|
||||
actual_compose_stdin = config_to_compose(
|
||||
@@ -1070,7 +1070,7 @@ def test_config_to_compose_env_file():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1080,15 +1080,15 @@ def test_config_to_compose_env_file():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
actual_compose_stdin = config_to_compose(
|
||||
PATH_TO_CONFIG,
|
||||
@@ -1108,7 +1108,7 @@ def test_config_to_compose_watch():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1118,15 +1118,15 @@ def test_config_to_compose_watch():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
|
||||
develop:
|
||||
watch:
|
||||
@@ -1155,7 +1155,7 @@ def test_config_to_compose_end_to_end():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1165,15 +1165,15 @@ def test_config_to_compose_end_to_end():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --prerelease=allow --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
|
||||
develop:
|
||||
watch:
|
||||
|
||||
Generated
+40
-40
@@ -489,7 +489,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-api"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cloudpickle", marker = "python_full_version >= '3.11'" },
|
||||
@@ -512,9 +512,9 @@ dependencies = [
|
||||
{ name = "uvicorn", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "watchfiles", marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/7c/37bd3f791dc00698b405288dc764e078ed143c7ecd65e2e72cc0e386d3f1/langgraph_api-0.3.0.tar.gz", hash = "sha256:b908c34ebb9c42c2f4fb17f3f3a71e39ee0dfb283b7028d73d4bc1f4681914e7", size = 255812, upload-time = "2025-08-21T00:49:28.347Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/c5/1bcaf07f3f973a4a540bd341f20dfc60f0fc3cac8e7c2dc606680cb1d11e/langgraph_api-0.4.1.tar.gz", hash = "sha256:6f12b8be914ad9a9f11e3b73278fc90aeb7b4988b0418afbdf686c6fd5577c74", size = 1579764, upload-time = "2025-08-26T12:23:48.172Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/06/34/cdbb0b5abf603fd01c91a63c9436fbe733f56cae448f42c7a1c7e3f4508e/langgraph_api-0.3.0-py3-none-any.whl", hash = "sha256:b76a7ced3bb559560b42a8d8f3cc7cff638c2022d0881d476caa1d511fb36a2e", size = 206012, upload-time = "2025-08-21T00:49:26.917Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/5e/b36e6035b52e06a9c0bd04030255a03b2444c9d2cad347b96193674cf4f5/langgraph_api-0.4.1-py3-none-any.whl", hash = "sha256:1f5f8d33bca30200e954e81416f4de26bce173a7f2e623b1e509d1b8a44b49a3", size = 1536988, upload-time = "2025-08-26T12:23:45.834Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -561,8 +561,8 @@ dev = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "click", specifier = ">=8.1.7" },
|
||||
{ name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.120,<0.4.0" },
|
||||
{ name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.6.8" },
|
||||
{ name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3,<0.5.0" },
|
||||
{ name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.7" },
|
||||
{ name = "langgraph-sdk", marker = "python_full_version >= '3.11'", specifier = ">=0.1.0" },
|
||||
{ name = "python-dotenv", marker = "extra == 'inmem'", specifier = ">=0.8.0" },
|
||||
]
|
||||
@@ -595,7 +595,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-runtime-inmem"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "blockbuster", marker = "python_full_version >= '3.11'" },
|
||||
@@ -605,27 +605,27 @@ dependencies = [
|
||||
{ name = "starlette", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "structlog", marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/94/02b58b1c137cfca6507f6c495e5794fd542b1f5269ef89fb662799be4b8c/langgraph_runtime_inmem-0.8.0.tar.gz", hash = "sha256:3082273f65650665b4a3875241721087fd51e675eb0227b18dc271839ce99594", size = 79510, upload-time = "2025-08-18T09:00:09.162Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2d/88/e5a0a776b2db76c114e3dc67eaa79135f008cc23c1af0802069acd9272d6/langgraph_runtime_inmem-0.9.0.tar.gz", hash = "sha256:7c5adfc98a5cce77c9d8413f606ce6edce315d15e4617a1191e066a0afe38802", size = 81401, upload-time = "2025-08-26T01:02:10.709Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/0e/b09c1aff0bcfdb1357be212b4a5d55f5b98644eae7fab6b115aa463e99fa/langgraph_runtime_inmem-0.8.0-py3-none-any.whl", hash = "sha256:85398321fc186618b0957c4d8629cc059fce2e7f57a4756ef83a75575791da1b", size = 31626, upload-time = "2025-08-18T09:00:08.256Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/e5/8da2e1cc9a97750a346b21b81fc52d2d4127c226775a3071436d6c67e97b/langgraph_runtime_inmem-0.9.0-py3-none-any.whl", hash = "sha256:541e3666d37038f15fa6c2fe34667ae649fd79913de50720241f4017f8b7b946", size = 33479, upload-time = "2025-08-26T01:02:09.522Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-sdk"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httpx", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "orjson", marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4d/3a/ea929b5b3827615802f020abdaa6d4a6f9d59ab764f65559fa6f87a6dda6/langgraph_sdk-0.2.2.tar.gz", hash = "sha256:9484e8071953df75d7aaf9845d82db3595e485af7d5dcc235c9b32c52362e1fc", size = 77981, upload-time = "2025-08-18T19:25:42.596Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4e/50/1f5e4d129e3969973674db01bf5dcb85e2233e5e4fdffa53eefff1399902/langgraph_sdk-0.2.3.tar.gz", hash = "sha256:17398aeae0f937cae1c8eb9027ada2969abdb50fe8ed3246c78f543b679cf959", size = 78468, upload-time = "2025-08-21T23:01:06.674Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/0d/dfa633c6b85e973e7d4383e9b92603b7e910e89768411daeb7777bfbae04/langgraph_sdk-0.2.2-py3-none-any.whl", hash = "sha256:1afbec01ade166f8b6ce18782875415422eb70dcb82852aeaa373e6152db4b82", size = 52017, upload-time = "2025-08-18T19:25:40.567Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/51/e6e3eb6b3cbad394d00cad128155781bc01e672ff01c5fc1c8a627b35a92/langgraph_sdk-0.2.3-py3-none-any.whl", hash = "sha256:059edfe2f62708c2e54239e170f5a33f796d456dbdbde64276c16cac8b97ba99", size = 52603, upload-time = "2025-08-21T23:01:05.379Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "langsmith"
|
||||
version = "0.4.15"
|
||||
version = "0.4.17"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httpx", marker = "python_full_version >= '3.11'" },
|
||||
@@ -636,9 +636,9 @@ dependencies = [
|
||||
{ name = "requests-toolbelt", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "zstandard", marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/7a/a17f61670bec8fea8194fb0fef79812d02fc7d2b9660eb2c9d2d514f265f/langsmith-0.4.15.tar.gz", hash = "sha256:c9a4ec4b7b0f5f70e7be204ee9c5b17db872cc3015cd04851e2703a0966b9ff1", size = 930330, upload-time = "2025-08-20T17:48:32.552Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/6e/6a76eed14cbf43db79030f8e348de2fdad904334967df34d1c162641679c/langsmith-0.4.17.tar.gz", hash = "sha256:c91e218cd96ee9a310f4685ea7f2b958a54aec919f0f368e694f42431e32c422", size = 931257, upload-time = "2025-08-26T01:42:47.394Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/07/05/fcd7ee91990399fb7e1042da4b196bc99174f5282c6cb266607770abe935/langsmith-0.4.15-py3-none-any.whl", hash = "sha256:633719f9fdafe4297459b0ab442f536d6c0734b36c186ded67343d335e5ea0b3", size = 375733, upload-time = "2025-08-20T17:48:30.254Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/4c/6f9106237cbeb193e3138393f7134310a38e0db9e6acfc1f43567dfa0559/langsmith-0.4.17-py3-none-any.whl", hash = "sha256:b824ef6a3a0669faf2bc61799e22af0b05a3a53d12cb9a448f611f861a69e991", size = 376351, upload-time = "2025-08-26T01:42:45.101Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1207,28 +1207,28 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.12.9"
|
||||
version = "0.12.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4a/45/2e403fa7007816b5fbb324cb4f8ed3c7402a927a0a0cb2b6279879a8bfdc/ruff-0.12.9.tar.gz", hash = "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", size = 5254702, upload-time = "2025-08-14T16:08:55.2Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3b/eb/8c073deb376e46ae767f4961390d17545e8535921d2f65101720ed8bd434/ruff-0.12.10.tar.gz", hash = "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", size = 5310076, upload-time = "2025-08-21T18:23:22.595Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/20/53bf098537adb7b6a97d98fcdebf6e916fcd11b2e21d15f8c171507909cc/ruff-0.12.9-py3-none-linux_armv6l.whl", hash = "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e", size = 11759705, upload-time = "2025-08-14T16:08:12.968Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/4d/c764ee423002aac1ec66b9d541285dd29d2c0640a8086c87de59ebbe80d5/ruff-0.12.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", size = 12527042, upload-time = "2025-08-14T16:08:16.54Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/45/cfcdf6d3eb5fc78a5b419e7e616d6ccba0013dc5b180522920af2897e1be/ruff-0.12.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", size = 11724457, upload-time = "2025-08-14T16:08:18.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/e6/44615c754b55662200c48bebb02196dbb14111b6e266ab071b7e7297b4ec/ruff-0.12.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", size = 11949446, upload-time = "2025-08-14T16:08:21.059Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/d1/9b7d46625d617c7df520d40d5ac6cdcdf20cbccb88fad4b5ecd476a6bb8d/ruff-0.12.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", size = 11566350, upload-time = "2025-08-14T16:08:23.433Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/20/b73132f66f2856bc29d2d263c6ca457f8476b0bbbe064dac3ac3337a270f/ruff-0.12.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", size = 13270430, upload-time = "2025-08-14T16:08:25.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/21/eaf3806f0a3d4c6be0a69d435646fba775b65f3f2097d54898b0fd4bb12e/ruff-0.12.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", size = 14264717, upload-time = "2025-08-14T16:08:27.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/82/1d0c53bd37dcb582b2c521d352fbf4876b1e28bc0d8894344198f6c9950d/ruff-0.12.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", size = 13684331, upload-time = "2025-08-14T16:08:30.352Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/2f/1c5cf6d8f656306d42a686f1e207f71d7cebdcbe7b2aa18e4e8a0cb74da3/ruff-0.12.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", size = 12739151, upload-time = "2025-08-14T16:08:32.55Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/09/25033198bff89b24d734e6479e39b1968e4c992e82262d61cdccaf11afb9/ruff-0.12.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", size = 12954992, upload-time = "2025-08-14T16:08:34.816Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/8e/d0dbf2f9dca66c2d7131feefc386523404014968cd6d22f057763935ab32/ruff-0.12.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", size = 12899569, upload-time = "2025-08-14T16:08:36.852Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/bd/b614d7c08515b1428ed4d3f1d4e3d687deffb2479703b90237682586fa66/ruff-0.12.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", size = 11751983, upload-time = "2025-08-14T16:08:39.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/d6/383e9f818a2441b1a0ed898d7875f11273f10882f997388b2b51cb2ae8b5/ruff-0.12.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", size = 11538635, upload-time = "2025-08-14T16:08:41.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/9c/56f869d314edaa9fc1f491706d1d8a47747b9d714130368fbd69ce9024e9/ruff-0.12.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", size = 12534346, upload-time = "2025-08-14T16:08:43.39Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/4b/d8b95c6795a6c93b439bc913ee7a94fda42bb30a79285d47b80074003ee7/ruff-0.12.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", size = 13017021, upload-time = "2025-08-14T16:08:45.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/c1/5f9a839a697ce1acd7af44836f7c2181cdae5accd17a5cb85fcbd694075e/ruff-0.12.9-py3-none-win32.whl", hash = "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", size = 11734785, upload-time = "2025-08-14T16:08:48.062Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/66/cdddc2d1d9a9f677520b7cfc490d234336f523d4b429c1298de359a3be08/ruff-0.12.9-py3-none-win_amd64.whl", hash = "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", size = 12840654, upload-time = "2025-08-14T16:08:50.158Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/e7/560d049d15585d6c201f9eeacd2fd130def3741323e5ccf123786e0e3c95/ruff-0.12.10-py3-none-linux_armv6l.whl", hash = "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", size = 11935161, upload-time = "2025-08-21T18:22:26.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/b0/ad2464922a1113c365d12b8f80ed70fcfb39764288ac77c995156080488d/ruff-0.12.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", size = 12660884, upload-time = "2025-08-21T18:22:30.925Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/f1/97f509b4108d7bae16c48389f54f005b62ce86712120fd8b2d8e88a7cb49/ruff-0.12.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", size = 11872754, upload-time = "2025-08-21T18:22:34.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/ad/44f606d243f744a75adc432275217296095101f83f966842063d78eee2d3/ruff-0.12.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", size = 12092276, upload-time = "2025-08-21T18:22:36.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/1f/ed6c265e199568010197909b25c896d66e4ef2c5e1c3808caf461f6f3579/ruff-0.12.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", size = 11734700, upload-time = "2025-08-21T18:22:39.822Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/c5/b21cde720f54a1d1db71538c0bc9b73dee4b563a7dd7d2e404914904d7f5/ruff-0.12.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", size = 13468783, upload-time = "2025-08-21T18:22:42.559Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9e/39369e6ac7f2a1848f22fb0b00b690492f20811a1ac5c1fd1d2798329263/ruff-0.12.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", size = 14436642, upload-time = "2025-08-21T18:22:45.612Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/03/5da8cad4b0d5242a936eb203b58318016db44f5c5d351b07e3f5e211bb89/ruff-0.12.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", size = 13859107, upload-time = "2025-08-21T18:22:48.886Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/19/dd7273b69bf7f93a070c9cec9494a94048325ad18fdcf50114f07e6bf417/ruff-0.12.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", size = 12886521, upload-time = "2025-08-21T18:22:51.567Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/1d/b4207ec35e7babaee62c462769e77457e26eb853fbdc877af29417033333/ruff-0.12.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", size = 13097528, upload-time = "2025-08-21T18:22:54.609Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/00/58f7b873b21114456e880b75176af3490d7a2836033779ca42f50de3b47a/ruff-0.12.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", size = 13080443, upload-time = "2025-08-21T18:22:57.413Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/8c/9e6660007fb10189ccb78a02b41691288038e51e4788bf49b0a60f740604/ruff-0.12.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60", size = 11896759, upload-time = "2025-08-21T18:23:00.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/4c/6d092bb99ea9ea6ebda817a0e7ad886f42a58b4501a7e27cd97371d0ba54/ruff-0.12.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", size = 11701463, upload-time = "2025-08-21T18:23:03.211Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/80/d982c55e91df981f3ab62559371380616c57ffd0172d96850280c2b04fa8/ruff-0.12.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", size = 12691603, upload-time = "2025-08-21T18:23:06.935Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/37/63a9c788bbe0b0850611669ec6b8589838faf2f4f959647f2d3e320383ae/ruff-0.12.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", size = 13164356, upload-time = "2025-08-21T18:23:10.225Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/d4/1aaa7fb201a74181989970ebccd12f88c0fc074777027e2a21de5a90657e/ruff-0.12.10-py3-none-win32.whl", hash = "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", size = 11896089, upload-time = "2025-08-21T18:23:14.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/14/2ad38fd4037daab9e023456a4a40ed0154e9971f8d6aed41bdea390aabd9/ruff-0.12.10-py3-none-win_amd64.whl", hash = "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", size = 13004616, upload-time = "2025-08-21T18:23:17.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1256,15 +1256,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.47.2"
|
||||
version = "0.47.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144, upload-time = "2025-08-24T13:36:42.122Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991, upload-time = "2025-08-24T13:36:40.887Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1335,11 +1335,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.14.1"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
"Bash(python:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(sed:*)",
|
||||
"Bash(awk:*)"
|
||||
"Bash(awk:*)",
|
||||
"Bash(uv run mypy:*)",
|
||||
"Bash(uv run:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ While LangGraph can be used standalone, it also integrates seamlessly with any L
|
||||
|
||||
## Additional resources
|
||||
|
||||
- [Guides](https://langchain-ai.github.io/langgraph/how-tos/): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
|
||||
- [Guides](https://langchain-ai.github.io/langgraph/guides/): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
|
||||
- [Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Detailed reference on core classes, methods, how to use the graph and checkpointing APIs, and higher-level prebuilt components.
|
||||
- [Examples](https://langchain-ai.github.io/langgraph/examples/): Guided examples on getting started with LangGraph.
|
||||
- [LangChain Forum](https://forum.langchain.com/): Connect with the community and share all of your technical questions, ideas, and feedback.
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
from collections.abc import Sequence
|
||||
from inspect import signature
|
||||
from typing import Any, Callable, cast
|
||||
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.messages import AIMessage, SystemMessage
|
||||
from langchain_core.tools import BaseTool
|
||||
|
||||
from langgraph.agent.types import (
|
||||
AgentJump,
|
||||
AgentMiddleware,
|
||||
AgentState,
|
||||
AgentUpdate,
|
||||
JumpTo,
|
||||
ModelRequest,
|
||||
ResponseFormat,
|
||||
)
|
||||
from langgraph.constants import END, START
|
||||
from langgraph.graph.state import StateGraph
|
||||
from langgraph.prebuilt.tool_node import ToolNode
|
||||
|
||||
|
||||
def create_agent(
|
||||
*,
|
||||
model: str | BaseChatModel,
|
||||
tools: Sequence[BaseTool | Callable] | ToolNode,
|
||||
system_prompt: str,
|
||||
middleware: Sequence[AgentMiddleware] = (),
|
||||
response_format: ResponseFormat | None = None,
|
||||
context_schema: type[Any] | None = None,
|
||||
) -> StateGraph[AgentState, None, AgentUpdate]:
|
||||
# init chat model
|
||||
if isinstance(model, str):
|
||||
try:
|
||||
from langchain.chat_models import ( # type: ignore[import-not-found]
|
||||
init_chat_model,
|
||||
)
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Please install langchain (`pip install langchain`) to "
|
||||
"use '<provider>:<model>' string syntax for `model` parameter."
|
||||
)
|
||||
|
||||
model = cast(BaseChatModel, init_chat_model(model))
|
||||
|
||||
if isinstance(tools, list):
|
||||
all_tools = [t for m in middleware for t in m.tools] + list(tools)
|
||||
tool_node = ToolNode(tools=all_tools)
|
||||
default_tools = tools
|
||||
else:
|
||||
# TODO: what do we do when middleware tools are specified, plus a ToolNode is used?
|
||||
tool_node = tools
|
||||
default_tools = list(tool_node.tools_by_name.values())
|
||||
|
||||
# validate middleware
|
||||
assert len({m.__class__.__name__ for m in middleware}) == len(middleware), (
|
||||
"Please remove duplicate middleware instances."
|
||||
) # this is just to keep the node names simple, we can change if needed
|
||||
middleware_w_before = [
|
||||
m
|
||||
for m in middleware
|
||||
if m.__class__.before_model is not AgentMiddleware.before_model
|
||||
]
|
||||
middleware_w_modify_model_request = [
|
||||
m
|
||||
for m in middleware
|
||||
if m.__class__.modify_model_request is not AgentMiddleware.modify_model_request
|
||||
]
|
||||
middleware_w_after = [
|
||||
m
|
||||
for m in middleware
|
||||
if m.__class__.after_model is not AgentMiddleware.after_model
|
||||
]
|
||||
|
||||
# create graph, add nodes
|
||||
graph = StateGraph(
|
||||
AgentState,
|
||||
input_schema=AgentUpdate,
|
||||
output_schema=AgentUpdate,
|
||||
context_schema=context_schema,
|
||||
)
|
||||
|
||||
def model_request(state: AgentState) -> AgentState:
|
||||
request = state.model_request or ModelRequest(
|
||||
model=model,
|
||||
tools=default_tools,
|
||||
system_prompt=system_prompt,
|
||||
response_format=response_format,
|
||||
messages=state.messages,
|
||||
tool_choice=None,
|
||||
)
|
||||
|
||||
# prepare messages
|
||||
print(request.system_prompt)
|
||||
if request.system_prompt:
|
||||
messages = [SystemMessage(request.system_prompt)] + request.messages
|
||||
else:
|
||||
messages = request.messages
|
||||
# call model
|
||||
if request.response_format:
|
||||
model_ = request.model.with_structured_output(
|
||||
request.response_format, include_raw=True
|
||||
)
|
||||
output = model_.invoke(
|
||||
messages, tools=request.tools, tool_choice=request.tool_choice
|
||||
)
|
||||
return {"messages": output["raw"], "response": output["parsed"]}
|
||||
else:
|
||||
model_ = request.model.bind_tools(
|
||||
request.tools,
|
||||
tool_choice=request.tool_choice,
|
||||
parallel_tool_calls=False,
|
||||
)
|
||||
output = model_.invoke(messages)
|
||||
if state.response is not None:
|
||||
return {"messages": output, "response": None}
|
||||
else:
|
||||
return {"messages": output}
|
||||
|
||||
graph.add_node("model_request", model_request)
|
||||
graph.add_node("tools", tool_node)
|
||||
|
||||
for m in middleware:
|
||||
if m.__class__.before_model is not AgentMiddleware.before_model:
|
||||
graph.add_node(
|
||||
f"{m.__class__.__name__}.before_model",
|
||||
m.before_model,
|
||||
input_schema=m.State,
|
||||
)
|
||||
if m.__class__.modify_model_request is not AgentMiddleware.modify_model_request:
|
||||
|
||||
def modify_model_request_node(state: AgentState) -> dict[str, ModelRequest]:
|
||||
# TODO assert request.tools in tools, or pass them to tool node
|
||||
|
||||
default_model_request = ModelRequest(
|
||||
model=model,
|
||||
tools=default_tools,
|
||||
system_prompt=system_prompt,
|
||||
response_format=response_format,
|
||||
messages=state.messages,
|
||||
tool_choice=None,
|
||||
)
|
||||
|
||||
return {
|
||||
"model_request": m.modify_model_request(
|
||||
state.model_request or default_model_request, state
|
||||
)
|
||||
}
|
||||
|
||||
graph.add_node(
|
||||
f"{m.__class__.__name__}.modify_model_request",
|
||||
modify_model_request_node,
|
||||
input_schema=m.State,
|
||||
)
|
||||
|
||||
if m.__class__.after_model is not AgentMiddleware.after_model:
|
||||
graph.add_node(
|
||||
f"{m.__class__.__name__}.after_model",
|
||||
m.after_model,
|
||||
input_schema=m.State,
|
||||
)
|
||||
|
||||
# add start edge
|
||||
first_node = (
|
||||
f"{middleware_w_before[0].__class__.__name__}.before_model"
|
||||
if middleware_w_before
|
||||
else f"{middleware_w_modify_model_request[0].__class__.__name__}.modify_model_request"
|
||||
if middleware_w_modify_model_request
|
||||
else "model_request"
|
||||
)
|
||||
last_node = (
|
||||
f"{middleware_w_after[0].__class__.__name__}.after_model"
|
||||
if middleware_w_after
|
||||
else "model_request"
|
||||
)
|
||||
graph.add_edge(START, first_node)
|
||||
|
||||
# add cond edges
|
||||
graph.add_conditional_edges(
|
||||
"tools",
|
||||
_make_tools_to_model_edge(tool_node, first_node),
|
||||
[first_node, END],
|
||||
)
|
||||
graph.add_conditional_edges(
|
||||
last_node, _make_model_to_tools_edge(first_node), [first_node, "tools", END]
|
||||
)
|
||||
|
||||
# add before model edges
|
||||
if middleware_w_before:
|
||||
for m1, m2 in zip(middleware_w_before, middleware_w_before[1:]):
|
||||
_add_middleware_edge(
|
||||
graph,
|
||||
m1.before_model,
|
||||
f"{m1.__class__.__name__}.before_model",
|
||||
f"{m2.__class__.__name__}.before_model",
|
||||
first_node,
|
||||
)
|
||||
_add_middleware_edge(
|
||||
graph,
|
||||
middleware_w_before[-1].before_model,
|
||||
f"{middleware_w_before[-1].__class__.__name__}.before_model",
|
||||
"model_request",
|
||||
first_node,
|
||||
)
|
||||
|
||||
# add modify model request edges
|
||||
if middleware_w_modify_model_request:
|
||||
for m1, m2 in zip(
|
||||
middleware_w_modify_model_request, middleware_w_modify_model_request[1:]
|
||||
):
|
||||
_add_middleware_edge(
|
||||
graph,
|
||||
m1.modify_model_request,
|
||||
f"{m1.__class__.__name__}.modify_model_request",
|
||||
f"{m2.__class__.__name__}.modify_model_request",
|
||||
first_node,
|
||||
)
|
||||
_add_middleware_edge(
|
||||
graph,
|
||||
middleware_w_modify_model_request[-1].modify_model_request,
|
||||
f"{middleware_w_modify_model_request[-1].__class__.__name__}.modify_model_request",
|
||||
"model_request",
|
||||
first_node,
|
||||
)
|
||||
|
||||
# add after model edges
|
||||
if middleware_w_after:
|
||||
graph.add_edge(
|
||||
"model_request", f"{middleware_w_after[-1].__class__.__name__}.after_model"
|
||||
)
|
||||
for idx in range(len(middleware_w_after) - 1, 0, -1):
|
||||
m1 = middleware_w_after[idx]
|
||||
m2 = middleware_w_after[idx - 1]
|
||||
_add_middleware_edge(
|
||||
graph,
|
||||
m1.after_model,
|
||||
f"{m1.__class__.__name__}.after_model",
|
||||
f"{m2.__class__.__name__}.after_model",
|
||||
first_node,
|
||||
)
|
||||
# _add_middleware_edge(
|
||||
# graph,
|
||||
# middleware_w_after[-1].after_model,
|
||||
# f"{middleware_w_after[-1].__class__.__name__}.after_model",
|
||||
# "model_request",
|
||||
# first_node,
|
||||
# )
|
||||
|
||||
return graph
|
||||
|
||||
|
||||
def _resolve_jump(jump_to: JumpTo | None, first_node: str) -> str | None:
|
||||
if jump_to == "model":
|
||||
return first_node
|
||||
elif jump_to:
|
||||
return jump_to
|
||||
|
||||
|
||||
def _make_model_to_tools_edge(first_node: str) -> Callable[[AgentState], str | None]:
|
||||
def model_to_tools(state: AgentState) -> str | None:
|
||||
if state.jump_to:
|
||||
return _resolve_jump(state.jump_to, first_node)
|
||||
message = state.messages[-1]
|
||||
if isinstance(message, AIMessage) and message.tool_calls:
|
||||
return "tools"
|
||||
|
||||
return END
|
||||
|
||||
return model_to_tools
|
||||
|
||||
|
||||
def _make_tools_to_model_edge(
|
||||
tool_node: ToolNode, next_node: str
|
||||
) -> Callable[[AgentState], str | None]:
|
||||
def tools_to_model(state: AgentState) -> str | None:
|
||||
ai_message = [m for m in state.messages if isinstance(m, AIMessage)][-1]
|
||||
if all(
|
||||
tool_node.tools_by_name[c["name"]].return_direct
|
||||
for c in ai_message.tool_calls
|
||||
if c["name"] in tool_node.tools_by_name
|
||||
):
|
||||
return END
|
||||
|
||||
return next_node
|
||||
|
||||
return tools_to_model
|
||||
|
||||
|
||||
def _add_middleware_edge(
|
||||
graph: StateGraph,
|
||||
method: Callable[[AgentState], AgentUpdate | AgentJump | None],
|
||||
name: str,
|
||||
default_destination: str,
|
||||
model_destination: str,
|
||||
) -> None:
|
||||
sig = signature(method)
|
||||
uses_jump = sig.return_annotation is AgentJump or AgentJump in getattr(
|
||||
sig.return_annotation, "__args__", ()
|
||||
)
|
||||
|
||||
if uses_jump:
|
||||
|
||||
def jump_edge(state: AgentState) -> str:
|
||||
return (
|
||||
_resolve_jump(state.jump_to, model_destination) or default_destination
|
||||
)
|
||||
|
||||
destinations = [default_destination, END, "tools"]
|
||||
if name != model_destination:
|
||||
destinations.append(model_destination)
|
||||
|
||||
graph.add_conditional_edges(name, jump_edge, destinations)
|
||||
else:
|
||||
graph.add_edge(name, default_destination)
|
||||
@@ -1,35 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
|
||||
from langgraph.agent.types import (
|
||||
AgentJump,
|
||||
AgentMiddleware,
|
||||
AgentState,
|
||||
AgentUpdate,
|
||||
ModelRequest,
|
||||
)
|
||||
|
||||
|
||||
class DynamicModelMiddleware(AgentMiddleware):
|
||||
"""Selects different models based on task complexity"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
basic_model: BaseChatModel,
|
||||
complex_model: BaseChatModel,
|
||||
message_threshold: int = 5,
|
||||
):
|
||||
self.basic_model = basic_model
|
||||
self.complex_model = complex_model
|
||||
self.message_threshold = message_threshold
|
||||
|
||||
def modify_model_request(
|
||||
self, request: ModelRequest, state: AgentState
|
||||
) -> ModelRequest:
|
||||
if len(state.messages) > self.message_threshold:
|
||||
request.model = self.complex_model
|
||||
else:
|
||||
request.model = self.basic_model
|
||||
return request
|
||||
@@ -1,102 +0,0 @@
|
||||
from langgraph.agent.types import AgentJump, AgentMiddleware, AgentState, AgentUpdate
|
||||
from langgraph.prebuilt.interrupt import (
|
||||
ActionRequest,
|
||||
HumanInterrupt,
|
||||
HumanInterruptConfig,
|
||||
HumanResponse,
|
||||
)
|
||||
from langgraph.types import interrupt
|
||||
|
||||
ToolInterruptConfig = dict[str, HumanInterruptConfig]
|
||||
|
||||
|
||||
class HumanInTheLoopMiddleware(AgentMiddleware):
|
||||
def __init__(
|
||||
self,
|
||||
tool_configs: ToolInterruptConfig,
|
||||
message_prefix: str = "Tool execution requires approval",
|
||||
):
|
||||
super().__init__()
|
||||
self.tool_configs = tool_configs
|
||||
self.message_prefix = message_prefix
|
||||
|
||||
def after_model(self, state: AgentState) -> AgentUpdate | AgentJump | None:
|
||||
messages = state.messages
|
||||
if not messages:
|
||||
return
|
||||
|
||||
last_message = messages[-1]
|
||||
|
||||
if not hasattr(last_message, "tool_calls") or not last_message.tool_calls:
|
||||
return
|
||||
|
||||
# Separate tool calls that need interrupts from those that don't
|
||||
interrupt_tool_calls = []
|
||||
auto_approved_tool_calls = []
|
||||
|
||||
for tool_call in last_message.tool_calls:
|
||||
tool_name = tool_call["name"]
|
||||
if tool_name in self.tool_configs:
|
||||
interrupt_tool_calls.append(tool_call)
|
||||
else:
|
||||
auto_approved_tool_calls.append(tool_call)
|
||||
|
||||
# If no interrupts needed, return early
|
||||
if not interrupt_tool_calls:
|
||||
return
|
||||
|
||||
approved_tool_calls = auto_approved_tool_calls.copy()
|
||||
|
||||
# Process all tool calls that need interrupts in parallel
|
||||
requests = []
|
||||
|
||||
for tool_call in interrupt_tool_calls:
|
||||
tool_name = tool_call["name"]
|
||||
tool_args = tool_call["args"]
|
||||
description = (
|
||||
f"{self.message_prefix}\n\nTool: {tool_name}\nArgs: {tool_args}"
|
||||
)
|
||||
tool_config = self.tool_configs[tool_name]
|
||||
|
||||
request: HumanInterrupt = {
|
||||
"action_request": ActionRequest(
|
||||
action=tool_name,
|
||||
args=tool_args,
|
||||
),
|
||||
"config": tool_config,
|
||||
"description": description,
|
||||
}
|
||||
requests.append(request)
|
||||
|
||||
responses: list[HumanResponse] = interrupt(requests)
|
||||
|
||||
for i, response in enumerate(responses):
|
||||
tool_call = interrupt_tool_calls[i]
|
||||
|
||||
if response["type"] == "accept":
|
||||
approved_tool_calls.append(tool_call)
|
||||
elif response["type"] == "edit":
|
||||
edited: ActionRequest = response["args"]
|
||||
new_tool_call = {
|
||||
"name": tool_call["name"],
|
||||
"args": edited["args"],
|
||||
"id": tool_call["id"],
|
||||
}
|
||||
approved_tool_calls.append(new_tool_call)
|
||||
elif response["type"] == "ignore":
|
||||
# NOTE: does not work with multiple interrupts
|
||||
return {"goto": "__end__"}
|
||||
elif response["type"] == "response":
|
||||
# NOTE: does not work with multiple interrupts
|
||||
tool_message = {
|
||||
"role": "tool",
|
||||
"tool_call_id": tool_call["id"],
|
||||
"content": response["args"],
|
||||
}
|
||||
return {"messages": [tool_message], "goto": "model"}
|
||||
else:
|
||||
raise ValueError(f"Unknown response type: {response['type']}")
|
||||
|
||||
last_message.tool_calls = approved_tool_calls
|
||||
|
||||
return {"messages": [last_message]}
|
||||
@@ -1,23 +0,0 @@
|
||||
import operator
|
||||
from dataclasses import dataclass
|
||||
from typing import Annotated
|
||||
|
||||
from langgraph.agent.types import AgentJump, AgentMiddleware, AgentState, AgentUpdate
|
||||
|
||||
|
||||
class ModelRequestLimitMiddleware(AgentMiddleware):
|
||||
"""Terminates after N model requests"""
|
||||
|
||||
@dataclass
|
||||
class State(AgentMiddleware.State):
|
||||
model_request_count: Annotated[int, operator.add] = 0
|
||||
|
||||
def __init__(self, max_requests: int = 10):
|
||||
self.max_requests = max_requests
|
||||
|
||||
def before_model(self, state: State) -> AgentUpdate | AgentJump | None:
|
||||
# TODO: want to be able to configure end behavior here
|
||||
if state.model_request_count == self.max_requests:
|
||||
return {"jump_to": "__end__"}
|
||||
|
||||
return {"model_request_count": 1}
|
||||
@@ -1,108 +0,0 @@
|
||||
import uuid
|
||||
from collections.abc import Sequence
|
||||
from typing import Callable, Iterable
|
||||
|
||||
from langchain_core.language_models import LanguageModelLike
|
||||
from langchain_core.messages import (
|
||||
AIMessage,
|
||||
AnyMessage,
|
||||
MessageLikeRepresentation,
|
||||
RemoveMessage,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.messages.utils import count_tokens_approximately
|
||||
|
||||
TokenCounter = Callable[[Iterable[MessageLikeRepresentation]], int]
|
||||
|
||||
|
||||
from langgraph.agent.types import AgentMiddleware, AgentState
|
||||
|
||||
DEFAULT_SUMMARY_PROMPT = """<role>
|
||||
Context Extraction Assistant
|
||||
</role>
|
||||
|
||||
<primary_objective>
|
||||
Your sole objective in this task is to extract the highest quality/most relevant context from the conversation history below.
|
||||
</primary_objective>
|
||||
|
||||
<objective_information>
|
||||
You're nearing the total number of input tokens you can accept, so you must extract the highest quality/most relevant pieces of information from your conversation history.
|
||||
This context will then overwrite the conversation history presented below. Because of this, ensure the context you extract is only the most important information to your overall goal.
|
||||
</objective_information>
|
||||
|
||||
<instructions>
|
||||
The conversation history below will be replaced with the context you extract in this step. Because of this, you must do your very best to extract and record all of the most important context from the conversation history.
|
||||
You want to ensure that you don't repeat any actions you've already completed, so the context you extract from the conversation history should be focused on the most important information to your overall goal.
|
||||
</instructions>
|
||||
|
||||
The user will message you with the full message history you'll be extracting context from, to then replace. Carefully read over it all, and think deeply about what information is most important to your overall goal that should be saved:
|
||||
|
||||
With all of this in mind, please carefully read over the entire conversation history, and extract the most important and relevant context to replace it so that you can free up space in the conversation history.
|
||||
Respond ONLY with the extracted context. Do not include any additional information, or text before or after the extracted context."""
|
||||
|
||||
|
||||
class SummarizationMiddleware(AgentMiddleware):
|
||||
def __init__(
|
||||
self,
|
||||
model: LanguageModelLike,
|
||||
max_tokens_before_summary: int | None = None,
|
||||
token_counter: TokenCounter = count_tokens_approximately,
|
||||
messages_to_leave: int = 20,
|
||||
summary_system_prompt: str = DEFAULT_SUMMARY_PROMPT,
|
||||
fake_tool_call_name: str = "summarize_convo",
|
||||
):
|
||||
super().__init__()
|
||||
self.model = model
|
||||
self.max_tokens_before_summary = max_tokens_before_summary
|
||||
self.token_counter = token_counter
|
||||
self.messages_to_leave = messages_to_leave
|
||||
self.summary_system_prompt = summary_system_prompt
|
||||
self.fake_tool_call_name = fake_tool_call_name
|
||||
|
||||
def before_model(self, state: AgentState) -> AgentState | None:
|
||||
messages = state.messages
|
||||
token_counts = self.token_counter(messages)
|
||||
# If token counts are less than max allowed, then end this hook early
|
||||
if token_counts < self.max_tokens_before_summary:
|
||||
return None
|
||||
# Otherwise, we create a summary!
|
||||
# Get messages that we want to create a summary for
|
||||
messages_to_summarize = messages[: -self.messages_to_leave]
|
||||
# Create summary text
|
||||
summary = self._summarize_messages(messages_to_summarize)
|
||||
# Create fake messages to add to history
|
||||
fake_tool_call_id = str(uuid.uuid4())
|
||||
fake_messages = [
|
||||
AIMessage(
|
||||
content="Looks like I'm running out of tokens. I'm going to summarize the conversation history to free up space.",
|
||||
tool_calls={
|
||||
"id": fake_tool_call_id,
|
||||
"name": self.fake_tool_call_name,
|
||||
"args": {
|
||||
"reasoning": "I'm running out of tokens. I'm going to summarize all of the messages since my last summary message to free up space.",
|
||||
},
|
||||
},
|
||||
),
|
||||
ToolMessage(tool_call_id=fake_tool_call_id, content=summary),
|
||||
]
|
||||
return {
|
||||
"messages": [RemoveMessage(id=m.id) for m in messages_to_summarize]
|
||||
+ fake_messages
|
||||
}
|
||||
|
||||
def _summarize_messages(self, messages_to_summarize: Sequence[AnyMessage]) -> str:
|
||||
system_message = self.summary_system_prompt
|
||||
user_message = self._format_messages(messages_to_summarize)
|
||||
response = self.model.invoke(
|
||||
[
|
||||
{"role": "system", "content": system_message},
|
||||
{"role": "user", "content": user_message},
|
||||
]
|
||||
)
|
||||
# Use new .text attribute when ready
|
||||
return response.content
|
||||
|
||||
@staticmethod
|
||||
def _format_messages(messages_to_summarize: Sequence[AnyMessage]) -> str:
|
||||
# TODO: better formatting logic
|
||||
return "\n".join([m.content for m in messages_to_summarize])
|
||||
@@ -1,84 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import cast
|
||||
|
||||
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
|
||||
from langchain_core.tools import BaseTool, tool
|
||||
|
||||
from langgraph.agent import create_agent
|
||||
from langgraph.agent.types import AgentJump, AgentMiddleware, ModelRequest
|
||||
|
||||
|
||||
@dataclass
|
||||
class SwarmAgent:
|
||||
name: str
|
||||
system_prompt: str
|
||||
tools: list[BaseTool]
|
||||
|
||||
|
||||
class MultiAgentMiddleware(AgentMiddleware):
|
||||
"""Multi agent middleware (enabling swarm like behavior).
|
||||
|
||||
TODOs:
|
||||
* Support create_agent for handoffs
|
||||
* Support handoff customization
|
||||
* do we want to include handoff messages / enable togglging
|
||||
* default active agent
|
||||
* handoff tool naming / descriptions
|
||||
"""
|
||||
|
||||
@dataclass
|
||||
class State(AgentMiddleware.State):
|
||||
active_agent: str | None = None
|
||||
|
||||
@staticmethod
|
||||
def _create_handoff_tools(agents: list[SwarmAgent]) -> list[BaseTool]:
|
||||
handoff_tools: list[BaseTool] = []
|
||||
|
||||
for agent in agents:
|
||||
|
||||
def handoff_tool() -> str:
|
||||
return f"Handing off to {agent.name}"
|
||||
|
||||
handoff_tools.append(
|
||||
tool(
|
||||
f"handoff_to_{agent.name}",
|
||||
description=f"Handoff tool to trigger a handoff to {agent.name}",
|
||||
)(handoff_tool)
|
||||
)
|
||||
|
||||
return handoff_tools
|
||||
|
||||
def __init__(self, agents: list[SwarmAgent]):
|
||||
self.agents: dict[str, SwarmAgent] = {agent.name: agent for agent in agents}
|
||||
self.handoff_tools = self._create_handoff_tools(agents)
|
||||
|
||||
def modify_model_request(self, request: ModelRequest, state: State) -> ModelRequest:
|
||||
if (active_agent := getattr(state, "active_agent", None)) is not None:
|
||||
agent = self.agents[active_agent]
|
||||
request.system_prompt = agent.system_prompt
|
||||
request.tools = agent.tools
|
||||
|
||||
request.tools.extend(self.handoff_tools)
|
||||
|
||||
return request
|
||||
|
||||
def after_model(self, state) -> State | None:
|
||||
# TODO: handle parallel handoffs, we don't do this currently
|
||||
|
||||
ai_msg: AIMessage = cast(AIMessage, state.messages[-1])
|
||||
if ai_msg.tool_calls:
|
||||
for call in ai_msg.tool_calls:
|
||||
if call["name"].startswith("handoff_to_"):
|
||||
active_agent = call["name"].replace("handoff_to_", "")
|
||||
return {
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
name=call["name"],
|
||||
content=f"Successfully transferred to {active_agent}",
|
||||
tool_call_id=call["id"],
|
||||
)
|
||||
],
|
||||
"active_agent": active_agent,
|
||||
"jump_to": "model",
|
||||
}
|
||||
return None
|
||||
@@ -1,44 +0,0 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Annotated, Any, Dict, List, cast
|
||||
|
||||
from langchain_core.messages import AIMessage
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from langgraph.agent.types import AgentJump, AgentMiddleware, AgentState, AgentUpdate
|
||||
|
||||
|
||||
class ToolCallLimitMiddleware(AgentMiddleware):
|
||||
"""Terminates after a specific tool is called N times"""
|
||||
|
||||
@dataclass
|
||||
class State(AgentMiddleware.State):
|
||||
important: Annotated[dict[str, int], Input, Output] = field(default_factory=dict)
|
||||
|
||||
@dataclass
|
||||
class InputState(AgentMiddleware.State):
|
||||
important: dict[str, int]
|
||||
|
||||
@dataclass
|
||||
class OutputState(AgentMiddleware.State):
|
||||
important: dict[str, int]
|
||||
|
||||
def __init__(self, tool_limits: dict[str, int]):
|
||||
self.tool_limits = tool_limits
|
||||
|
||||
def after_model(self, state: State) -> AgentUpdate | AgentJump | None:
|
||||
ai_msg: AIMessage = cast(AIMessage, state.messages[-1])
|
||||
|
||||
tool_calls = {}
|
||||
for call in ai_msg.tool_calls or []:
|
||||
tool_calls[call["name"]] = tool_calls.get(call["name"], 0) + 1
|
||||
|
||||
aggregate_calls = state.tool_call_count.copy()
|
||||
for tool_name in tool_calls.keys():
|
||||
aggregate_calls[tool_name] = aggregate_calls.get(tool_name, 0) + 1
|
||||
|
||||
for tool_name, max_calls in self.tool_limits.items():
|
||||
count = aggregate_calls.get(tool_name, 0)
|
||||
if count == max_calls:
|
||||
return {"tool_call_count": aggregate_calls, "jump_to": "__end__"}
|
||||
|
||||
return {"tool_call_count": aggregate_calls}
|
||||
@@ -1,62 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.messages import AnyMessage
|
||||
from langchain_core.tools import BaseTool
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langgraph.channels.ephemeral_value import EphemeralValue
|
||||
from langgraph.graph.message import Messages, add_messages
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
ResponseFormat = dict | type[BaseModel]
|
||||
JumpTo = Literal["tools", "model", "__end__"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModelRequest:
|
||||
model: BaseChatModel
|
||||
system_prompt: str
|
||||
messages: list[AnyMessage] # excluding system prompt
|
||||
tool_choice: Any
|
||||
tools: list[BaseTool]
|
||||
response_format: ResponseFormat | None
|
||||
|
||||
|
||||
@dataclass
|
||||
class AgentState:
|
||||
messages: Annotated[list[AnyMessage], add_messages]
|
||||
model_request: Annotated[ModelRequest | None, EphemeralValue] = None
|
||||
jump_to: Annotated[JumpTo | None, EphemeralValue] = None
|
||||
response: dict | None = None
|
||||
|
||||
|
||||
class AgentMiddleware:
|
||||
class State(AgentState):
|
||||
pass
|
||||
|
||||
tools: list[BaseTool]
|
||||
|
||||
def before_model(self, state: State) -> AgentUpdate | AgentJump | None:
|
||||
pass
|
||||
|
||||
def modify_model_request(self, request: ModelRequest, state: State) -> ModelRequest:
|
||||
return request
|
||||
|
||||
def after_model(self, state: State) -> AgentUpdate | AgentJump | None:
|
||||
pass
|
||||
|
||||
|
||||
class AgentUpdate(TypedDict, total=False):
|
||||
messages: Messages
|
||||
response: dict
|
||||
|
||||
|
||||
class AgentJump(TypedDict, total=False):
|
||||
messages: Messages
|
||||
jump_to: JumpTo
|
||||
@@ -100,16 +100,25 @@ def task(
|
||||
retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None,
|
||||
cache_policy: CachePolicy[Callable[P, str | bytes]] | None = None,
|
||||
**kwargs: Unpack[DeprecatedKwargs],
|
||||
) -> Callable[
|
||||
[Callable[P, Awaitable[T]] | Callable[P, T]],
|
||||
_TaskFunction[P, T],
|
||||
]: ...
|
||||
) -> Callable[[Callable[P, T]], _TaskFunction[P, T]]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def task(
|
||||
__func_or_none__: Callable[P, Awaitable[T]] | Callable[P, T],
|
||||
) -> _TaskFunction[P, T]: ...
|
||||
*,
|
||||
name: str | None = None,
|
||||
retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None,
|
||||
cache_policy: CachePolicy[Callable[P, str | bytes]] | None = None,
|
||||
**kwargs: Unpack[DeprecatedKwargs],
|
||||
) -> Callable[[Callable[P, Awaitable[T]]], _TaskFunction[P, T]]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def task(__func_or_none__: Callable[P, T]) -> _TaskFunction[P, T]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def task(__func_or_none__: Callable[P, Awaitable[T]]) -> _TaskFunction[P, T]: ...
|
||||
|
||||
|
||||
def task(
|
||||
@@ -120,7 +129,8 @@ def task(
|
||||
cache_policy: CachePolicy[Callable[P, str | bytes]] | None = None,
|
||||
**kwargs: Unpack[DeprecatedKwargs],
|
||||
) -> (
|
||||
Callable[[Callable[P, Awaitable[T]] | Callable[P, T]], _TaskFunction[P, T]]
|
||||
Callable[[Callable[P, T]], _TaskFunction[P, T]]
|
||||
| Callable[[Callable[P, Awaitable[T]]], _TaskFunction[P, T]]
|
||||
| _TaskFunction[P, T]
|
||||
):
|
||||
"""Define a LangGraph task using the `task` decorator.
|
||||
|
||||
@@ -25,7 +25,7 @@ from typing import (
|
||||
|
||||
from langchain_core.runnables import Runnable, RunnableConfig
|
||||
from pydantic import BaseModel, TypeAdapter
|
||||
from typing_extensions import Self, Unpack, is_typeddict
|
||||
from typing_extensions import NotRequired, Required, Self, Unpack, is_typeddict
|
||||
|
||||
from langgraph._internal._constants import (
|
||||
INTERRUPT,
|
||||
@@ -155,7 +155,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
graph = StateGraph(state_schema=State, context_schema=Context)
|
||||
|
||||
def node(state: State, runtime: Runtime[Context]) -> dict:
|
||||
r = runtie.context.get("r", 1.0)
|
||||
r = runtime.context.get("r", 1.0)
|
||||
x = state["x"][-1]
|
||||
next_value = x * r * (1 - x)
|
||||
return {"x": next_value}
|
||||
@@ -1334,6 +1334,12 @@ def _get_channel(
|
||||
def _get_channel(
|
||||
name: str, annotation: Any, *, allow_managed: bool = True
|
||||
) -> BaseChannel | ManagedValueSpec:
|
||||
# Strip out Required and NotRequired wrappers
|
||||
if hasattr(annotation, "__origin__") and annotation.__origin__ in (
|
||||
Required,
|
||||
NotRequired,
|
||||
):
|
||||
annotation = annotation.__args__[0]
|
||||
if manager := _is_field_managed_value(name, annotation):
|
||||
if allow_managed:
|
||||
return manager
|
||||
|
||||
@@ -194,7 +194,7 @@ def local_read(
|
||||
for c, v in task.writes:
|
||||
if c in select:
|
||||
updated[c].append(v)
|
||||
if fresh and updated:
|
||||
if fresh:
|
||||
# apply writes
|
||||
local_channels: dict[str, BaseChannel] = {}
|
||||
for k in channels:
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
# serializer version: 1
|
||||
# name: test_create_agent_diagram
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
__start__ --> model_request;
|
||||
model_request -.-> __end__;
|
||||
model_request -.-> tools;
|
||||
tools -.-> __end__;
|
||||
tools -.-> model_request;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.1
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopOne_before_model(NoopOne.before_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopOne_before_model --> model_request;
|
||||
__start__ --> NoopOne_before_model;
|
||||
model_request -.-> __end__;
|
||||
model_request -.-> tools;
|
||||
tools -.-> NoopOne_before_model;
|
||||
tools -.-> __end__;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.2
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopOne_before_model(NoopOne.before_model)
|
||||
NoopTwo_before_model(NoopTwo.before_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopOne_before_model --> NoopTwo_before_model;
|
||||
NoopTwo_before_model --> model_request;
|
||||
__start__ --> NoopOne_before_model;
|
||||
model_request -.-> __end__;
|
||||
model_request -.-> tools;
|
||||
tools -.-> NoopOne_before_model;
|
||||
tools -.-> __end__;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.3
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopOne_before_model(NoopOne.before_model)
|
||||
NoopTwo_before_model(NoopTwo.before_model)
|
||||
NoopThree_before_model(NoopThree.before_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopOne_before_model --> NoopTwo_before_model;
|
||||
NoopThree_before_model --> model_request;
|
||||
NoopTwo_before_model --> NoopThree_before_model;
|
||||
__start__ --> NoopOne_before_model;
|
||||
model_request -.-> __end__;
|
||||
model_request -.-> tools;
|
||||
tools -.-> NoopOne_before_model;
|
||||
tools -.-> __end__;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.4
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopFour_after_model(NoopFour.after_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopFour_after_model -.-> __end__;
|
||||
NoopFour_after_model -.-> tools;
|
||||
__start__ --> model_request;
|
||||
model_request --> NoopFour_after_model;
|
||||
tools -.-> __end__;
|
||||
tools -.-> model_request;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.5
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopFour_after_model(NoopFour.after_model)
|
||||
NoopFive_after_model(NoopFive.after_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopFive_after_model --> NoopFour_after_model;
|
||||
NoopFour_after_model -.-> __end__;
|
||||
NoopFour_after_model -.-> tools;
|
||||
__start__ --> model_request;
|
||||
model_request --> NoopFive_after_model;
|
||||
tools -.-> __end__;
|
||||
tools -.-> model_request;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.6
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopFour_after_model(NoopFour.after_model)
|
||||
NoopFive_after_model(NoopFive.after_model)
|
||||
NoopSix_after_model(NoopSix.after_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopFive_after_model --> NoopFour_after_model;
|
||||
NoopFour_after_model -.-> __end__;
|
||||
NoopFour_after_model -.-> tools;
|
||||
NoopSix_after_model --> NoopFive_after_model;
|
||||
__start__ --> model_request;
|
||||
model_request --> NoopSix_after_model;
|
||||
tools -.-> __end__;
|
||||
tools -.-> model_request;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.7
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopSeven_before_model(NoopSeven.before_model)
|
||||
NoopSeven_after_model(NoopSeven.after_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopSeven_after_model -.-> __end__;
|
||||
NoopSeven_after_model -.-> tools;
|
||||
NoopSeven_before_model --> model_request;
|
||||
__start__ --> NoopSeven_before_model;
|
||||
model_request --> NoopSeven_after_model;
|
||||
tools -.-> NoopSeven_before_model;
|
||||
tools -.-> __end__;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.8
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopSeven_before_model(NoopSeven.before_model)
|
||||
NoopSeven_after_model(NoopSeven.after_model)
|
||||
NoopEight_before_model(NoopEight.before_model)
|
||||
NoopEight_after_model(NoopEight.after_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopEight_after_model --> NoopSeven_after_model;
|
||||
NoopEight_before_model --> model_request;
|
||||
NoopSeven_after_model -.-> __end__;
|
||||
NoopSeven_after_model -.-> tools;
|
||||
NoopSeven_before_model --> NoopEight_before_model;
|
||||
__start__ --> NoopSeven_before_model;
|
||||
model_request --> NoopEight_after_model;
|
||||
tools -.-> NoopSeven_before_model;
|
||||
tools -.-> __end__;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_diagram.9
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopSeven_before_model(NoopSeven.before_model)
|
||||
NoopSeven_after_model(NoopSeven.after_model)
|
||||
NoopEight_before_model(NoopEight.before_model)
|
||||
NoopEight_after_model(NoopEight.after_model)
|
||||
NoopNine_before_model(NoopNine.before_model)
|
||||
NoopNine_after_model(NoopNine.after_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopEight_after_model --> NoopSeven_after_model;
|
||||
NoopEight_before_model --> NoopNine_before_model;
|
||||
NoopNine_after_model --> NoopEight_after_model;
|
||||
NoopNine_before_model --> model_request;
|
||||
NoopSeven_after_model -.-> __end__;
|
||||
NoopSeven_after_model -.-> tools;
|
||||
NoopSeven_before_model --> NoopEight_before_model;
|
||||
__start__ --> NoopSeven_before_model;
|
||||
model_request --> NoopNine_after_model;
|
||||
tools -.-> NoopSeven_before_model;
|
||||
tools -.-> __end__;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_create_agent_jump[memory]
|
||||
'''
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
graph TD;
|
||||
__start__([<p>__start__</p>]):::first
|
||||
model_request(model_request)
|
||||
tools(tools)
|
||||
NoopSeven_before_model(NoopSeven.before_model)
|
||||
NoopSeven_after_model(NoopSeven.after_model)
|
||||
NoopEight_before_model(NoopEight.before_model)
|
||||
NoopEight_after_model(NoopEight.after_model)
|
||||
__end__([<p>__end__</p>]):::last
|
||||
NoopEight_after_model --> NoopSeven_after_model;
|
||||
NoopEight_before_model -.-> NoopSeven_before_model;
|
||||
NoopEight_before_model -.-> __end__;
|
||||
NoopEight_before_model -.-> model_request;
|
||||
NoopEight_before_model -.-> tools;
|
||||
NoopSeven_after_model -.-> __end__;
|
||||
NoopSeven_after_model -.-> tools;
|
||||
NoopSeven_before_model --> NoopEight_before_model;
|
||||
__start__ --> NoopSeven_before_model;
|
||||
model_request --> NoopEight_after_model;
|
||||
tools -.-> NoopSeven_before_model;
|
||||
tools -.-> __end__;
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
'''
|
||||
# ---
|
||||
@@ -1,298 +0,0 @@
|
||||
from langchain_core.messages import AIMessage, ToolCall
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from langgraph.agent import create_agent
|
||||
from langgraph.agent.types import AgentJump, AgentMiddleware
|
||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.constants import END
|
||||
from tests.fake_chat import FakeChatModel
|
||||
from tests.messages import _AnyIdToolMessage
|
||||
|
||||
|
||||
def test_create_agent_diagram(
|
||||
snapshot: SnapshotAssertion,
|
||||
):
|
||||
class NoopOne(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopTwo(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopThree(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopFour(AgentMiddleware):
|
||||
def after_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopFive(AgentMiddleware):
|
||||
def after_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopSix(AgentMiddleware):
|
||||
def after_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopSeven(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
pass
|
||||
|
||||
def after_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopEight(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
pass
|
||||
|
||||
def after_model(self, state):
|
||||
pass
|
||||
|
||||
class NoopNine(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
pass
|
||||
|
||||
def after_model(self, state):
|
||||
pass
|
||||
|
||||
agent_zero = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
)
|
||||
|
||||
assert agent_zero.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_one = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopOne()],
|
||||
)
|
||||
|
||||
assert agent_one.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_two = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopOne(), NoopTwo()],
|
||||
)
|
||||
|
||||
assert agent_two.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_three = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopOne(), NoopTwo(), NoopThree()],
|
||||
)
|
||||
|
||||
assert agent_three.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_four = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopFour()],
|
||||
)
|
||||
|
||||
assert agent_four.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_five = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopFour(), NoopFive()],
|
||||
)
|
||||
|
||||
assert agent_five.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_six = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopFour(), NoopFive(), NoopSix()],
|
||||
)
|
||||
|
||||
assert agent_six.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_seven = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopSeven()],
|
||||
)
|
||||
|
||||
assert agent_seven.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_eight = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopSeven(), NoopEight()],
|
||||
)
|
||||
|
||||
assert agent_eight.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
agent_nine = create_agent(
|
||||
model=FakeChatModel(messages=[]),
|
||||
tools=[],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopSeven(), NoopEight(), NoopNine()],
|
||||
)
|
||||
|
||||
assert agent_nine.compile().get_graph().draw_mermaid() == snapshot
|
||||
|
||||
|
||||
def test_create_agent_invoke(
|
||||
snapshot: SnapshotAssertion,
|
||||
sync_checkpointer: BaseCheckpointSaver,
|
||||
):
|
||||
calls = []
|
||||
|
||||
class NoopSeven(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
calls.append("NoopSeven.before_model")
|
||||
|
||||
def modify_model_request(self, request, state):
|
||||
calls.append("NoopSeven.modify_model_request")
|
||||
return request
|
||||
|
||||
def after_model(self, state):
|
||||
calls.append("NoopSeven.after_model")
|
||||
|
||||
class NoopEight(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
calls.append("NoopEight.before_model")
|
||||
|
||||
def modify_model_request(self, request, state):
|
||||
calls.append("NoopEight.modify_model_request")
|
||||
return request
|
||||
|
||||
def after_model(self, state):
|
||||
calls.append("NoopEight.after_model")
|
||||
|
||||
def my_tool(input: str) -> str:
|
||||
"""A great tool"""
|
||||
calls.append("my_tool")
|
||||
return input.upper()
|
||||
|
||||
agent_one = create_agent(
|
||||
model=FakeChatModel(
|
||||
messages=[
|
||||
AIMessage(
|
||||
"",
|
||||
id="ai1",
|
||||
tool_calls=[ToolCall(id="1", name="my_tool", args={"input": "yo"})],
|
||||
),
|
||||
AIMessage(id="ai2", content="Hello, how can I assist you today?"),
|
||||
]
|
||||
),
|
||||
tools=[my_tool],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopSeven(), NoopEight()],
|
||||
).compile(checkpointer=sync_checkpointer)
|
||||
|
||||
thread1 = {"configurable": {"thread_id": "1"}}
|
||||
assert agent_one.invoke({"messages": []}, thread1) == {
|
||||
"messages": [
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={},
|
||||
response_metadata={},
|
||||
id="ai1",
|
||||
tool_calls=[
|
||||
{
|
||||
"name": "my_tool",
|
||||
"args": {"input": "yo"},
|
||||
"id": "1",
|
||||
"type": "tool_call",
|
||||
}
|
||||
],
|
||||
),
|
||||
_AnyIdToolMessage(content="YO", name="my_tool", tool_call_id="1"),
|
||||
AIMessage(
|
||||
content="Hello, how can I assist you today?",
|
||||
additional_kwargs={},
|
||||
response_metadata={},
|
||||
id="ai2",
|
||||
),
|
||||
],
|
||||
}
|
||||
assert calls == [
|
||||
"NoopSeven.before_model",
|
||||
"NoopEight.before_model",
|
||||
"NoopSeven.modify_model_request",
|
||||
"NoopEight.modify_model_request",
|
||||
"NoopEight.after_model",
|
||||
"NoopSeven.after_model",
|
||||
"my_tool",
|
||||
"NoopSeven.before_model",
|
||||
"NoopEight.before_model",
|
||||
"NoopSeven.modify_model_request",
|
||||
"NoopEight.modify_model_request",
|
||||
"NoopEight.after_model",
|
||||
"NoopSeven.after_model",
|
||||
]
|
||||
|
||||
|
||||
def test_create_agent_jump(
|
||||
snapshot: SnapshotAssertion,
|
||||
sync_checkpointer: BaseCheckpointSaver,
|
||||
):
|
||||
calls = []
|
||||
|
||||
class NoopSeven(AgentMiddleware):
|
||||
def before_model(self, state):
|
||||
calls.append("NoopSeven.before_model")
|
||||
|
||||
def modify_model_request(self, request, state):
|
||||
calls.append("NoopSeven.modify_model_request")
|
||||
return request
|
||||
|
||||
def after_model(self, state):
|
||||
calls.append("NoopSeven.after_model")
|
||||
|
||||
class NoopEight(AgentMiddleware):
|
||||
def before_model(self, state) -> AgentJump:
|
||||
calls.append("NoopEight.before_model")
|
||||
return {"jump_to": END}
|
||||
|
||||
def modify_model_request(self, request, state):
|
||||
calls.append("NoopEight.modify_model_request")
|
||||
return request
|
||||
|
||||
def after_model(self, state):
|
||||
calls.append("NoopEight.after_model")
|
||||
|
||||
def my_tool(input: str) -> str:
|
||||
"""A great tool"""
|
||||
calls.append("my_tool")
|
||||
return input.upper()
|
||||
|
||||
agent_one = create_agent(
|
||||
model=FakeChatModel(
|
||||
messages=[
|
||||
AIMessage(
|
||||
"",
|
||||
id="ai1",
|
||||
tool_calls=[ToolCall(id="1", name="my_tool", args={"input": "yo"})],
|
||||
),
|
||||
AIMessage(id="ai2", content="Hello, how can I assist you today?"),
|
||||
]
|
||||
),
|
||||
tools=[my_tool],
|
||||
system_prompt="You are a helpful assistant.",
|
||||
middleware=[NoopSeven(), NoopEight()],
|
||||
).compile(checkpointer=sync_checkpointer)
|
||||
|
||||
if isinstance(sync_checkpointer, InMemorySaver):
|
||||
assert agent_one.get_graph().draw_mermaid() == snapshot
|
||||
|
||||
thread1 = {"configurable": {"thread_id": "1"}}
|
||||
assert agent_one.invoke({"messages": []}, thread1) == {"messages": []}
|
||||
assert calls == ["NoopSeven.before_model", "NoopEight.before_model"]
|
||||
@@ -10,6 +10,7 @@ from langchain_core.runnables import RunnableConfig
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import NotRequired, Required, TypedDict
|
||||
|
||||
from langgraph.channels.binop import BinaryOperatorAggregate
|
||||
from langgraph.graph.state import StateGraph, _get_node_name, _warn_invalid_state_schema
|
||||
|
||||
|
||||
@@ -137,7 +138,7 @@ def test_state_schema_optional_values(total_: bool):
|
||||
class InputState(SomeParentState, total=total_): # type: ignore
|
||||
val1: str
|
||||
val2: Optional[str]
|
||||
val3: Required[str]
|
||||
val3: Required[Annotated[dict, operator.or_]]
|
||||
val4: NotRequired[dict]
|
||||
val5: Annotated[Required[str], "foo"]
|
||||
val6: Annotated[NotRequired[str], "bar"]
|
||||
@@ -159,6 +160,8 @@ def test_state_schema_optional_values(total_: bool):
|
||||
graph = builder.compile()
|
||||
json_schema = graph.get_input_jsonschema()
|
||||
|
||||
assert isinstance(graph.channels["val3"], BinaryOperatorAggregate)
|
||||
|
||||
if total_ is False:
|
||||
expected_required = set()
|
||||
expected_optional = {"val2", "val1"}
|
||||
|
||||
Generated
+219
-219
@@ -194,15 +194,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.13.4"
|
||||
version = "4.13.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "soupsieve" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/2e/3e5079847e653b1f6dc647aa24549d68c6addb4c595cc0d902d1b19308ad/beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695", size = 622954, upload-time = "2025-08-24T14:06:13.168Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113, upload-time = "2025-08-24T14:06:14.884Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -448,97 +448,97 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.10.4"
|
||||
version = "7.10.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/4e/08b493f1f1d8a5182df0044acc970799b58a8d289608e0d891a03e9d269a/coverage-7.10.4.tar.gz", hash = "sha256:25f5130af6c8e7297fd14634955ba9e1697f47143f289e2a23284177c0061d27", size = 823798, upload-time = "2025-08-17T00:26:43.314Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/61/83/153f54356c7c200013a752ce1ed5448573dca546ce125801afca9e1ac1a4/coverage-7.10.5.tar.gz", hash = "sha256:f2e57716a78bc3ae80b2207be0709a3b2b63b9f2dcf9740ee6ac03588a2015b6", size = 821662, upload-time = "2025-08-23T14:42:44.78Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/f4/350759710db50362685f922259c140592dba15eb4e2325656a98413864d9/coverage-7.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d92d6edb0ccafd20c6fbf9891ca720b39c2a6a4b4a6f9cf323ca2c986f33e475", size = 216403, upload-time = "2025-08-17T00:24:19.083Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/7e/e467c2bb4d5ecfd166bfd22c405cce4c50de2763ba1d78e2729c59539a42/coverage-7.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7202da14dc0236884fcc45665ffb2d79d4991a53fbdf152ab22f69f70923cc22", size = 216802, upload-time = "2025-08-17T00:24:21.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/ab/2accdd1ccfe63b890e5eb39118f63c155202df287798364868a2884a50af/coverage-7.10.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ada418633ae24ec8d0fcad5efe6fc7aa3c62497c6ed86589e57844ad04365674", size = 243558, upload-time = "2025-08-17T00:24:23.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/04/c14c33d0cfc0f4db6b3504d01a47f4c798563d932a836fd5f2dbc0521d3d/coverage-7.10.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b828e33eca6c3322adda3b5884456f98c435182a44917ded05005adfa1415500", size = 245370, upload-time = "2025-08-17T00:24:24.858Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/71/147053061f1f51c1d3b3d040c3cb26876964a3a0dca0765d2441411ca568/coverage-7.10.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:802793ba397afcfdbe9f91f89d65ae88b958d95edc8caf948e1f47d8b6b2b606", size = 247228, upload-time = "2025-08-17T00:24:26.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/92/7ef882205d4d4eb502e6154ee7122c1a1b1ce3f29d0166921e0fb550a5d3/coverage-7.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d0b23512338c54101d3bf7a1ab107d9d75abda1d5f69bc0887fd079253e4c27e", size = 245270, upload-time = "2025-08-17T00:24:27.424Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/3d/297a20603abcc6c7d89d801286eb477b0b861f3c5a4222730f1c9837be3e/coverage-7.10.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f36b7dcf72d06a8c5e2dd3aca02be2b1b5db5f86404627dff834396efce958f2", size = 243287, upload-time = "2025-08-17T00:24:28.697Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/f9/b04111438f41f1ddd5dc88706d5f8064ae5bb962203c49fe417fa23a362d/coverage-7.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fce316c367a1dc2c411821365592eeb335ff1781956d87a0410eae248188ba51", size = 244164, upload-time = "2025-08-17T00:24:30.393Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/e5/c7d9eb7a9ea66cf92d069077719fb2b07782dcd7050b01a9b88766b52154/coverage-7.10.4-cp310-cp310-win32.whl", hash = "sha256:8c5dab29fc8070b3766b5fc85f8d89b19634584429a2da6d42da5edfadaf32ae", size = 218917, upload-time = "2025-08-17T00:24:31.67Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/30/4d9d3b81f5a836b31a7428b8a25e6d490d4dca5ff2952492af130153c35c/coverage-7.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:4b0d114616f0fccb529a1817457d5fb52a10e106f86c5fb3b0bd0d45d0d69b93", size = 219822, upload-time = "2025-08-17T00:24:32.89Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/ba/2c9817e62018e7d480d14f684c160b3038df9ff69c5af7d80e97d143e4d1/coverage-7.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:05d5f98ec893d4a2abc8bc5f046f2f4367404e7e5d5d18b83de8fde1093ebc4f", size = 216514, upload-time = "2025-08-17T00:24:34.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/5a/093412a959a6b6261446221ba9fb23bb63f661a5de70b5d130763c87f916/coverage-7.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9267efd28f8994b750d171e58e481e3bbd69e44baed540e4c789f8e368b24b88", size = 216914, upload-time = "2025-08-17T00:24:35.881Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/1f/2fdf4a71cfe93b07eae845ebf763267539a7d8b7e16b062f959d56d7e433/coverage-7.10.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4456a039fdc1a89ea60823d0330f1ac6f97b0dbe9e2b6fb4873e889584b085fb", size = 247308, upload-time = "2025-08-17T00:24:37.61Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/16/33f6cded458e84f008b9f6bc379609a6a1eda7bffe349153b9960803fc11/coverage-7.10.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c2bfbd2a9f7e68a21c5bd191be94bfdb2691ac40d325bac9ef3ae45ff5c753d9", size = 249241, upload-time = "2025-08-17T00:24:38.919Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/98/9c18e47c889be58339ff2157c63b91a219272503ee32b49d926eea2337f2/coverage-7.10.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab7765f10ae1df7e7fe37de9e64b5a269b812ee22e2da3f84f97b1c7732a0d8", size = 251346, upload-time = "2025-08-17T00:24:40.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/07/00a6c0d53e9a22d36d8e95ddd049b860eef8f4b9fd299f7ce34d8e323356/coverage-7.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a09b13695166236e171ec1627ff8434b9a9bae47528d0ba9d944c912d33b3d2", size = 249037, upload-time = "2025-08-17T00:24:41.904Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/0e/1e1b944d6a6483d07bab5ef6ce063fcf3d0cc555a16a8c05ebaab11f5607/coverage-7.10.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5c9e75dfdc0167d5675e9804f04a56b2cf47fb83a524654297000b578b8adcb7", size = 247090, upload-time = "2025-08-17T00:24:43.193Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/43/2ce5ab8a728b8e25ced077111581290ffaef9efaf860a28e25435ab925cf/coverage-7.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c751261bfe6481caba15ec005a194cb60aad06f29235a74c24f18546d8377df0", size = 247732, upload-time = "2025-08-17T00:24:44.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/f3/706c4a24f42c1c5f3a2ca56637ab1270f84d9e75355160dc34d5e39bb5b7/coverage-7.10.4-cp311-cp311-win32.whl", hash = "sha256:051c7c9e765f003c2ff6e8c81ccea28a70fb5b0142671e4e3ede7cebd45c80af", size = 218961, upload-time = "2025-08-17T00:24:46.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/aa/6b9ea06e0290bf1cf2a2765bba89d561c5c563b4e9db8298bf83699c8b67/coverage-7.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:1a647b152f10be08fb771ae4a1421dbff66141e3d8ab27d543b5eb9ea5af8e52", size = 219851, upload-time = "2025-08-17T00:24:48.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/be/f0dc9ad50ee183369e643cd7ed8f2ef5c491bc20b4c3387cbed97dd6e0d1/coverage-7.10.4-cp311-cp311-win_arm64.whl", hash = "sha256:b09b9e4e1de0d406ca9f19a371c2beefe3193b542f64a6dd40cfcf435b7d6aa0", size = 218530, upload-time = "2025-08-17T00:24:50.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/4a/781c9e4dd57cabda2a28e2ce5b00b6be416015265851060945a5ed4bd85e/coverage-7.10.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a1f0264abcabd4853d4cb9b3d164adbf1565da7dab1da1669e93f3ea60162d79", size = 216706, upload-time = "2025-08-17T00:24:51.528Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/8c/51255202ca03d2e7b664770289f80db6f47b05138e06cce112b3957d5dfd/coverage-7.10.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:536cbe6b118a4df231b11af3e0f974a72a095182ff8ec5f4868c931e8043ef3e", size = 216939, upload-time = "2025-08-17T00:24:53.171Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/7f/df11131483698660f94d3c847dc76461369782d7a7644fcd72ac90da8fd0/coverage-7.10.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9a4c0d84134797b7bf3f080599d0cd501471f6c98b715405166860d79cfaa97e", size = 248429, upload-time = "2025-08-17T00:24:54.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/fa/13ac5eda7300e160bf98f082e75f5c5b4189bf3a883dd1ee42dbedfdc617/coverage-7.10.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7c155fc0f9cee8c9803ea0ad153ab6a3b956baa5d4cd993405dc0b45b2a0b9e0", size = 251178, upload-time = "2025-08-17T00:24:56.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/bc/f63b56a58ad0bec68a840e7be6b7ed9d6f6288d790760647bb88f5fea41e/coverage-7.10.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5f2ab6e451d4b07855d8bcf063adf11e199bff421a4ba57f5bb95b7444ca62", size = 252313, upload-time = "2025-08-17T00:24:57.692Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/b6/79338f1ea27b01266f845afb4485976211264ab92407d1c307babe3592a7/coverage-7.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:685b67d99b945b0c221be0780c336b303a7753b3e0ec0d618c795aada25d5e7a", size = 250230, upload-time = "2025-08-17T00:24:59.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/93/3b24f1da3e0286a4dc5832427e1d448d5296f8287464b1ff4a222abeeeb5/coverage-7.10.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0c079027e50c2ae44da51c2e294596cbc9dbb58f7ca45b30651c7e411060fc23", size = 248351, upload-time = "2025-08-17T00:25:00.676Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/5f/d59412f869e49dcc5b89398ef3146c8bfaec870b179cc344d27932e0554b/coverage-7.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3749aa72b93ce516f77cf5034d8e3c0dfd45c6e8a163a602ede2dc5f9a0bb927", size = 249788, upload-time = "2025-08-17T00:25:02.354Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/52/04a3b733f40a0cc7c4a5b9b010844111dbf906df3e868b13e1ce7b39ac31/coverage-7.10.4-cp312-cp312-win32.whl", hash = "sha256:fecb97b3a52fa9bcd5a7375e72fae209088faf671d39fae67261f37772d5559a", size = 219131, upload-time = "2025-08-17T00:25:03.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/dd/12909fc0b83888197b3ec43a4ac7753589591c08d00d9deda4158df2734e/coverage-7.10.4-cp312-cp312-win_amd64.whl", hash = "sha256:26de58f355626628a21fe6a70e1e1fad95702dafebfb0685280962ae1449f17b", size = 219939, upload-time = "2025-08-17T00:25:05.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/c7/058bb3220fdd6821bada9685eadac2940429ab3c97025ce53549ff423cc1/coverage-7.10.4-cp312-cp312-win_arm64.whl", hash = "sha256:67e8885408f8325198862bc487038a4980c9277d753cb8812510927f2176437a", size = 218572, upload-time = "2025-08-17T00:25:06.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/b0/4a3662de81f2ed792a4e425d59c4ae50d8dd1d844de252838c200beed65a/coverage-7.10.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b8e1d2015d5dfdbf964ecef12944c0c8c55b885bb5c0467ae8ef55e0e151233", size = 216735, upload-time = "2025-08-17T00:25:08.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/e8/e2dcffea01921bfffc6170fb4406cffb763a3b43a047bbd7923566708193/coverage-7.10.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:25735c299439018d66eb2dccf54f625aceb78645687a05f9f848f6e6c751e169", size = 216982, upload-time = "2025-08-17T00:25:10.384Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/59/cc89bb6ac869704d2781c2f5f7957d07097c77da0e8fdd4fd50dbf2ac9c0/coverage-7.10.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:715c06cb5eceac4d9b7cdf783ce04aa495f6aff657543fea75c30215b28ddb74", size = 247981, upload-time = "2025-08-17T00:25:11.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/23/3da089aa177ceaf0d3f96754ebc1318597822e6387560914cc480086e730/coverage-7.10.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e017ac69fac9aacd7df6dc464c05833e834dc5b00c914d7af9a5249fcccf07ef", size = 250584, upload-time = "2025-08-17T00:25:13.483Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/82/e8693c368535b4e5fad05252a366a1794d481c79ae0333ed943472fd778d/coverage-7.10.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bad180cc40b3fccb0f0e8c702d781492654ac2580d468e3ffc8065e38c6c2408", size = 251856, upload-time = "2025-08-17T00:25:15.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/19/8b9cb13292e602fa4135b10a26ac4ce169a7fc7c285ff08bedd42ff6acca/coverage-7.10.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:becbdcd14f685fada010a5f792bf0895675ecf7481304fe159f0cd3f289550bd", size = 250015, upload-time = "2025-08-17T00:25:16.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/e7/e5903990ce089527cf1c4f88b702985bd65c61ac245923f1ff1257dbcc02/coverage-7.10.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0b485ca21e16a76f68060911f97ebbe3e0d891da1dbbce6af7ca1ab3f98b9097", size = 247908, upload-time = "2025-08-17T00:25:18.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/c9/7d464f116df1df7fe340669af1ddbe1a371fc60f3082ff3dc837c4f1f2ab/coverage-7.10.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6c1d098ccfe8e1e0a1ed9a0249138899948afd2978cbf48eb1cc3fcd38469690", size = 249525, upload-time = "2025-08-17T00:25:20.141Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/42/722e0cdbf6c19e7235c2020837d4e00f3b07820fd012201a983238cc3a30/coverage-7.10.4-cp313-cp313-win32.whl", hash = "sha256:8630f8af2ca84b5c367c3df907b1706621abe06d6929f5045fd628968d421e6e", size = 219173, upload-time = "2025-08-17T00:25:21.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/7e/aa70366f8275955cd51fa1ed52a521c7fcebcc0fc279f53c8c1ee6006dfe/coverage-7.10.4-cp313-cp313-win_amd64.whl", hash = "sha256:f68835d31c421736be367d32f179e14ca932978293fe1b4c7a6a49b555dff5b2", size = 219969, upload-time = "2025-08-17T00:25:23.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/96/c39d92d5aad8fec28d4606556bfc92b6fee0ab51e4a548d9b49fb15a777c/coverage-7.10.4-cp313-cp313-win_arm64.whl", hash = "sha256:6eaa61ff6724ca7ebc5326d1fae062d85e19b38dd922d50903702e6078370ae7", size = 218601, upload-time = "2025-08-17T00:25:25.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/13/34d549a6177bd80fa5db758cb6fd3057b7ad9296d8707d4ab7f480b0135f/coverage-7.10.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:702978108876bfb3d997604930b05fe769462cc3000150b0e607b7b444f2fd84", size = 217445, upload-time = "2025-08-17T00:25:27.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/c0/433da866359bf39bf595f46d134ff2d6b4293aeea7f3328b6898733b0633/coverage-7.10.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e8f978e8c5521d9c8f2086ac60d931d583fab0a16f382f6eb89453fe998e2484", size = 217676, upload-time = "2025-08-17T00:25:28.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/d7/2b99aa8737f7801fd95222c79a4ebc8c5dd4460d4bed7ef26b17a60c8d74/coverage-7.10.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:df0ac2ccfd19351411c45e43ab60932b74472e4648b0a9edf6a3b58846e246a9", size = 259002, upload-time = "2025-08-17T00:25:30.065Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/cf/86432b69d57debaef5abf19aae661ba8f4fcd2882fa762e14added4bd334/coverage-7.10.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:73a0d1aaaa3796179f336448e1576a3de6fc95ff4f07c2d7251d4caf5d18cf8d", size = 261178, upload-time = "2025-08-17T00:25:31.517Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/78/85176593f4aa6e869cbed7a8098da3448a50e3fac5cb2ecba57729a5220d/coverage-7.10.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:873da6d0ed6b3ffc0bc01f2c7e3ad7e2023751c0d8d86c26fe7322c314b031dc", size = 263402, upload-time = "2025-08-17T00:25:33.339Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/1d/57a27b6789b79abcac0cc5805b31320d7a97fa20f728a6a7c562db9a3733/coverage-7.10.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c6446c75b0e7dda5daa876a1c87b480b2b52affb972fedd6c22edf1aaf2e00ec", size = 260957, upload-time = "2025-08-17T00:25:34.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/e5/3e5ddfd42835c6def6cd5b2bdb3348da2e34c08d9c1211e91a49e9fd709d/coverage-7.10.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6e73933e296634e520390c44758d553d3b573b321608118363e52113790633b9", size = 258718, upload-time = "2025-08-17T00:25:36.259Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/0b/d364f0f7ef111615dc4e05a6ed02cac7b6f2ac169884aa57faeae9eb5fa0/coverage-7.10.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52073d4b08d2cb571234c8a71eb32af3c6923149cf644a51d5957ac128cf6aa4", size = 259848, upload-time = "2025-08-17T00:25:37.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/c6/bbea60a3b309621162e53faf7fac740daaf083048ea22077418e1ecaba3f/coverage-7.10.4-cp313-cp313t-win32.whl", hash = "sha256:e24afb178f21f9ceb1aefbc73eb524769aa9b504a42b26857243f881af56880c", size = 219833, upload-time = "2025-08-17T00:25:39.252Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/a5/f9f080d49cfb117ddffe672f21eab41bd23a46179a907820743afac7c021/coverage-7.10.4-cp313-cp313t-win_amd64.whl", hash = "sha256:be04507ff1ad206f4be3d156a674e3fb84bbb751ea1b23b142979ac9eebaa15f", size = 220897, upload-time = "2025-08-17T00:25:40.772Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/89/49a3fc784fa73d707f603e586d84a18c2e7796707044e9d73d13260930b7/coverage-7.10.4-cp313-cp313t-win_arm64.whl", hash = "sha256:f3e3ff3f69d02b5dad67a6eac68cc9c71ae343b6328aae96e914f9f2f23a22e2", size = 219160, upload-time = "2025-08-17T00:25:42.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/22/525f84b4cbcff66024d29f6909d7ecde97223f998116d3677cfba0d115b5/coverage-7.10.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a59fe0af7dd7211ba595cf7e2867458381f7e5d7b4cffe46274e0b2f5b9f4eb4", size = 216717, upload-time = "2025-08-17T00:25:43.875Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/58/213577f77efe44333a416d4bcb251471e7f64b19b5886bb515561b5ce389/coverage-7.10.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a6c35c5b70f569ee38dc3350cd14fdd0347a8b389a18bb37538cc43e6f730e6", size = 216994, upload-time = "2025-08-17T00:25:45.405Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/85/34ac02d0985a09472f41b609a1d7babc32df87c726c7612dc93d30679b5a/coverage-7.10.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:acb7baf49f513554c4af6ef8e2bd6e8ac74e6ea0c7386df8b3eb586d82ccccc4", size = 248038, upload-time = "2025-08-17T00:25:46.981Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/4f/2140305ec93642fdaf988f139813629cbb6d8efa661b30a04b6f7c67c31e/coverage-7.10.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a89afecec1ed12ac13ed203238b560cbfad3522bae37d91c102e690b8b1dc46c", size = 250575, upload-time = "2025-08-17T00:25:48.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/b5/41b5784180b82a083c76aeba8f2c72ea1cb789e5382157b7dc852832aea2/coverage-7.10.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:480442727f464407d8ade6e677b7f21f3b96a9838ab541b9a28ce9e44123c14e", size = 251927, upload-time = "2025-08-17T00:25:50.881Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/ca/c1dd063e50b71f5aea2ebb27a1c404e7b5ecf5714c8b5301f20e4e8831ac/coverage-7.10.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a89bf193707f4a17f1ed461504031074d87f035153239f16ce86dfb8f8c7ac76", size = 249930, upload-time = "2025-08-17T00:25:52.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/66/d8907408612ffee100d731798e6090aedb3ba766ecf929df296c1a7ee4fb/coverage-7.10.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:3ddd912c2fc440f0fb3229e764feec85669d5d80a988ff1b336a27d73f63c818", size = 247862, upload-time = "2025-08-17T00:25:54.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/db/53cd8ec8b1c9c52d8e22a25434785bfc2d1e70c0cfb4d278a1326c87f741/coverage-7.10.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a538944ee3a42265e61c7298aeba9ea43f31c01271cf028f437a7b4075592cf", size = 249360, upload-time = "2025-08-17T00:25:55.833Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/75/5ec0a28ae4a0804124ea5a5becd2b0fa3adf30967ac656711fb5cdf67c60/coverage-7.10.4-cp314-cp314-win32.whl", hash = "sha256:fd2e6002be1c62476eb862b8514b1ba7e7684c50165f2a8d389e77da6c9a2ebd", size = 219449, upload-time = "2025-08-17T00:25:57.984Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/ab/66e2ee085ec60672bf5250f11101ad8143b81f24989e8c0e575d16bb1e53/coverage-7.10.4-cp314-cp314-win_amd64.whl", hash = "sha256:ec113277f2b5cf188d95fb66a65c7431f2b9192ee7e6ec9b72b30bbfb53c244a", size = 220246, upload-time = "2025-08-17T00:25:59.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/3b/00b448d385f149143190846217797d730b973c3c0ec2045a7e0f5db3a7d0/coverage-7.10.4-cp314-cp314-win_arm64.whl", hash = "sha256:9744954bfd387796c6a091b50d55ca7cac3d08767795b5eec69ad0f7dbf12d38", size = 218825, upload-time = "2025-08-17T00:26:01.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/2e/55e20d3d1ce00b513efb6fd35f13899e1c6d4f76c6cbcc9851c7227cd469/coverage-7.10.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5af4829904dda6aabb54a23879f0f4412094ba9ef153aaa464e3c1b1c9bc98e6", size = 217462, upload-time = "2025-08-17T00:26:03.014Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/b3/aab1260df5876f5921e2c57519e73a6f6eeacc0ae451e109d44ee747563e/coverage-7.10.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7bba5ed85e034831fac761ae506c0644d24fd5594727e174b5a73aff343a7508", size = 217675, upload-time = "2025-08-17T00:26:04.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/23/1cfe2aa50c7026180989f0bfc242168ac7c8399ccc66eb816b171e0ab05e/coverage-7.10.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d57d555b0719834b55ad35045de6cc80fc2b28e05adb6b03c98479f9553b387f", size = 259176, upload-time = "2025-08-17T00:26:06.159Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/72/5882b6aeed3f9de7fc4049874fd7d24213bf1d06882f5c754c8a682606ec/coverage-7.10.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ba62c51a72048bb1ea72db265e6bd8beaabf9809cd2125bbb5306c6ce105f214", size = 261341, upload-time = "2025-08-17T00:26:08.137Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/70/a0c76e3087596ae155f8e71a49c2c534c58b92aeacaf4d9d0cbbf2dde53b/coverage-7.10.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0acf0c62a6095f07e9db4ec365cc58c0ef5babb757e54745a1aa2ea2a2564af1", size = 263600, upload-time = "2025-08-17T00:26:11.045Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/5f/27e4cd4505b9a3c05257fb7fc509acbc778c830c450cb4ace00bf2b7bda7/coverage-7.10.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e1033bf0f763f5cf49ffe6594314b11027dcc1073ac590b415ea93463466deec", size = 261036, upload-time = "2025-08-17T00:26:12.693Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/d6/cf2ae3a7f90ab226ea765a104c4e76c5126f73c93a92eaea41e1dc6a1892/coverage-7.10.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:92c29eff894832b6a40da1789b1f252305af921750b03ee4535919db9179453d", size = 258794, upload-time = "2025-08-17T00:26:14.261Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/b1/39f222eab0d78aa2001cdb7852aa1140bba632db23a5cfd832218b496d6c/coverage-7.10.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:822c4c830989c2093527e92acd97be4638a44eb042b1bdc0e7a278d84a070bd3", size = 259946, upload-time = "2025-08-17T00:26:15.899Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/b2/49d82acefe2fe7c777436a3097f928c7242a842538b190f66aac01f29321/coverage-7.10.4-cp314-cp314t-win32.whl", hash = "sha256:e694d855dac2e7cf194ba33653e4ba7aad7267a802a7b3fc4347d0517d5d65cd", size = 220226, upload-time = "2025-08-17T00:26:17.566Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/b0/afb942b6b2fc30bdbc7b05b087beae11c2b0daaa08e160586cf012b6ad70/coverage-7.10.4-cp314-cp314t-win_amd64.whl", hash = "sha256:efcc54b38ef7d5bfa98050f220b415bc5bb3d432bd6350a861cf6da0ede2cdcd", size = 221346, upload-time = "2025-08-17T00:26:19.311Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/66/e0531c9d1525cb6eac5b5733c76f27f3053ee92665f83f8899516fea6e76/coverage-7.10.4-cp314-cp314t-win_arm64.whl", hash = "sha256:6f3a3496c0fa26bfac4ebc458747b778cff201c8ae94fa05e1391bab0dbc473c", size = 219368, upload-time = "2025-08-17T00:26:21.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/61/4e38d86d31a268778d69bb3fd1fc88e0c7a78ffdee48f2b5d9e028a3dce5/coverage-7.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:48fd4d52600c2a9d5622e52dfae674a7845c5e1dceaf68b88c99feb511fbcfd6", size = 216393, upload-time = "2025-08-17T00:26:22.648Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/16/5c2fdb1d213f57e0ff107738397aff68582fa90a6575ca165b49eae5a809/coverage-7.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:56217b470d09d69e6b7dcae38200f95e389a77db801cb129101697a4553b18b6", size = 216779, upload-time = "2025-08-17T00:26:24.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/99/3aca6b4028e3667ccfbaef9cfd9dca8d85eb14deee7868373cc48cbee553/coverage-7.10.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:44ac3f21a6e28c5ff7f7a47bca5f87885f6a1e623e637899125ba47acd87334d", size = 243214, upload-time = "2025-08-17T00:26:26.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/33/27a7d2557f85001b2edb6a2f14037851f87ca7d69a4ca79460e1859f4c7f/coverage-7.10.4-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3387739d72c84d17b4d2f7348749cac2e6700e7152026912b60998ee9a40066b", size = 245037, upload-time = "2025-08-17T00:26:28.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/68/92c0e18d36d34c774cb5053c9413188c27f8b3f9587e315193a30c1695ce/coverage-7.10.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f111ff20d9a6348e0125be892608e33408dd268f73b020940dfa8511ad05503", size = 246809, upload-time = "2025-08-17T00:26:29.828Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/22/f0618594010903401e782459c100755af3f275ea86d49b0d4f3afa3658d9/coverage-7.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:01a852f0a9859734b018a3f483cc962d0b381d48d350b1a0c47d618c73a0c398", size = 244695, upload-time = "2025-08-17T00:26:31.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/45/e704923a037a4a38a3c13ae6405c31236db2d274307ab28fd1a23b961cad/coverage-7.10.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:225111dd06759ba4e37cee4c0b4f3df2b15c879e9e3c37bf986389300b9917c3", size = 242766, upload-time = "2025-08-17T00:26:33.425Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/b7/4dc6f2b41aa907ae330ed841deb49c9487f6ec5072a577fc3a3b284fe855/coverage-7.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2178d4183bd1ba608f0bb12e71e55838ba1b7dbb730264f8b08de9f8ef0c27d0", size = 243723, upload-time = "2025-08-17T00:26:35.688Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/62/0e58abc2ce2d9a5b906dd1c08802864f756365843c413aebf0184148ddfb/coverage-7.10.4-cp39-cp39-win32.whl", hash = "sha256:93d175fe81913aee7a6ea430abbdf2a79f1d9fd451610e12e334e4fe3264f563", size = 218927, upload-time = "2025-08-17T00:26:37.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/3e/4668a5b5601450d9c8aa71cc4f7e6c6c259350e577c758b894443598322a/coverage-7.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:2221a823404bb941c7721cf0ef55ac6ee5c25d905beb60c0bba5e5e85415d353", size = 219838, upload-time = "2025-08-17T00:26:39.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/78/983efd23200921d9edb6bd40512e1aa04af553d7d5a171e50f9b2b45d109/coverage-7.10.4-py3-none-any.whl", hash = "sha256:065d75447228d05121e5c938ca8f0e91eed60a1eb2d1258d42d5084fecfc3302", size = 208365, upload-time = "2025-08-17T00:26:41.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/70/e77b0061a6c7157bfce645c6b9a715a08d4c86b3360a7b3252818080b817/coverage-7.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c6a5c3414bfc7451b879141ce772c546985163cf553f08e0f135f0699a911801", size = 216774, upload-time = "2025-08-23T14:40:26.301Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/08/2a79de5ecf37ee40f2d898012306f11c161548753391cec763f92647837b/coverage-7.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bc8e4d99ce82f1710cc3c125adc30fd1487d3cf6c2cd4994d78d68a47b16989a", size = 217175, upload-time = "2025-08-23T14:40:29.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/57/0171d69a699690149a6ba6a4eb702814448c8d617cf62dbafa7ce6bfdf63/coverage-7.10.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:02252dc1216e512a9311f596b3169fad54abcb13827a8d76d5630c798a50a754", size = 243931, upload-time = "2025-08-23T14:40:30.735Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/06/3a67662c55656702bd398a727a7f35df598eb11104fcb34f1ecbb070291a/coverage-7.10.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:73269df37883e02d460bee0cc16be90509faea1e3bd105d77360b512d5bb9c33", size = 245740, upload-time = "2025-08-23T14:40:32.302Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/f4/f8763aabf4dc30ef0d0012522d312f0b7f9fede6246a1f27dbcc4a1e523c/coverage-7.10.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f8a81b0614642f91c9effd53eec284f965577591f51f547a1cbeb32035b4c2f", size = 247600, upload-time = "2025-08-23T14:40:33.66Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/31/6632219a9065e1b83f77eda116fed4c76fb64908a6a9feae41816dab8237/coverage-7.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6a29f8e0adb7f8c2b95fa2d4566a1d6e6722e0a637634c6563cb1ab844427dd9", size = 245640, upload-time = "2025-08-23T14:40:35.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/e2/3dba9b86037b81649b11d192bb1df11dde9a81013e434af3520222707bc8/coverage-7.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fcf6ab569436b4a647d4e91accba12509ad9f2554bc93d3aee23cc596e7f99c3", size = 243659, upload-time = "2025-08-23T14:40:36.815Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/b9/57170bd9f3e333837fc24ecc88bc70fbc2eb7ccfd0876854b0c0407078c3/coverage-7.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:90dc3d6fb222b194a5de60af8d190bedeeddcbc7add317e4a3cd333ee6b7c879", size = 244537, upload-time = "2025-08-23T14:40:38.737Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/1c/93ac36ef1e8b06b8d5777393a3a40cb356f9f3dab980be40a6941e443588/coverage-7.10.5-cp310-cp310-win32.whl", hash = "sha256:414a568cd545f9dc75f0686a0049393de8098414b58ea071e03395505b73d7a8", size = 219285, upload-time = "2025-08-23T14:40:40.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/95/23252277e6e5fe649d6cd3ed3f35d2307e5166de4e75e66aa7f432abc46d/coverage-7.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:e551f9d03347196271935fd3c0c165f0e8c049220280c1120de0084d65e9c7ff", size = 220185, upload-time = "2025-08-23T14:40:42.026Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/f2/336d34d2fc1291ca7c18eeb46f64985e6cef5a1a7ef6d9c23720c6527289/coverage-7.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c177e6ffe2ebc7c410785307758ee21258aa8e8092b44d09a2da767834f075f2", size = 216890, upload-time = "2025-08-23T14:40:43.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/ea/92448b07cc1cf2b429d0ce635f59cf0c626a5d8de21358f11e92174ff2a6/coverage-7.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:14d6071c51ad0f703d6440827eaa46386169b5fdced42631d5a5ac419616046f", size = 217287, upload-time = "2025-08-23T14:40:45.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/ba/ad5b36537c5179c808d0ecdf6e4aa7630b311b3c12747ad624dcd43a9b6b/coverage-7.10.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:61f78c7c3bc272a410c5ae3fde7792b4ffb4acc03d35a7df73ca8978826bb7ab", size = 247683, upload-time = "2025-08-23T14:40:46.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/e5/fe3bbc8d097029d284b5fb305b38bb3404895da48495f05bff025df62770/coverage-7.10.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f39071caa126f69d63f99b324fb08c7b1da2ec28cbb1fe7b5b1799926492f65c", size = 249614, upload-time = "2025-08-23T14:40:48.082Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/9c/a1c89a8c8712799efccb32cd0a1ee88e452f0c13a006b65bb2271f1ac767/coverage-7.10.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343a023193f04d46edc46b2616cdbee68c94dd10208ecd3adc56fcc54ef2baa1", size = 251719, upload-time = "2025-08-23T14:40:49.349Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/be/5576b5625865aa95b5633315f8f4142b003a70c3d96e76f04487c3b5cc95/coverage-7.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:585ffe93ae5894d1ebdee69fc0b0d4b7c75d8007983692fb300ac98eed146f78", size = 249411, upload-time = "2025-08-23T14:40:50.624Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/0a/e39a113d4209da0dbbc9385608cdb1b0726a4d25f78672dc51c97cfea80f/coverage-7.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0ef4e66f006ed181df29b59921bd8fc7ed7cd6a9289295cd8b2824b49b570df", size = 247466, upload-time = "2025-08-23T14:40:52.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/cb/aebb2d8c9e3533ee340bea19b71c5b76605a0268aa49808e26fe96ec0a07/coverage-7.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb7b0bbf7cc1d0453b843eca7b5fa017874735bef9bfdfa4121373d2cc885ed6", size = 248104, upload-time = "2025-08-23T14:40:54.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/e6/26570d6ccce8ff5de912cbfd268e7f475f00597cb58da9991fa919c5e539/coverage-7.10.5-cp311-cp311-win32.whl", hash = "sha256:1d043a8a06987cc0c98516e57c4d3fc2c1591364831e9deb59c9e1b4937e8caf", size = 219327, upload-time = "2025-08-23T14:40:55.424Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/79/5f48525e366e518b36e66167e3b6e5db6fd54f63982500c6a5abb9d3dfbd/coverage-7.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:fefafcca09c3ac56372ef64a40f5fe17c5592fab906e0fdffd09543f3012ba50", size = 220213, upload-time = "2025-08-23T14:40:56.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/3c/9058128b7b0bf333130c320b1eb1ae485623014a21ee196d68f7737f8610/coverage-7.10.5-cp311-cp311-win_arm64.whl", hash = "sha256:7e78b767da8b5fc5b2faa69bb001edafcd6f3995b42a331c53ef9572c55ceb82", size = 218893, upload-time = "2025-08-23T14:40:58.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/8e/40d75c7128f871ea0fd829d3e7e4a14460cad7c3826e3b472e6471ad05bd/coverage-7.10.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c2d05c7e73c60a4cecc7d9b60dbfd603b4ebc0adafaef371445b47d0f805c8a9", size = 217077, upload-time = "2025-08-23T14:40:59.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/a8/f333f4cf3fb5477a7f727b4d603a2eb5c3c5611c7fe01329c2e13b23b678/coverage-7.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:32ddaa3b2c509778ed5373b177eb2bf5662405493baeff52278a0b4f9415188b", size = 217310, upload-time = "2025-08-23T14:41:00.628Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/2c/fbecd8381e0a07d1547922be819b4543a901402f63930313a519b937c668/coverage-7.10.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dd382410039fe062097aa0292ab6335a3f1e7af7bba2ef8d27dcda484918f20c", size = 248802, upload-time = "2025-08-23T14:41:02.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/bc/1011da599b414fb6c9c0f34086736126f9ff71f841755786a6b87601b088/coverage-7.10.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7fa22800f3908df31cea6fb230f20ac49e343515d968cc3a42b30d5c3ebf9b5a", size = 251550, upload-time = "2025-08-23T14:41:03.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/6f/b5c03c0c721c067d21bc697accc3642f3cef9f087dac429c918c37a37437/coverage-7.10.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f366a57ac81f5e12797136552f5b7502fa053c861a009b91b80ed51f2ce651c6", size = 252684, upload-time = "2025-08-23T14:41:04.85Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/50/d474bc300ebcb6a38a1047d5c465a227605d6473e49b4e0d793102312bc5/coverage-7.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1dc8f1980a272ad4a6c84cba7981792344dad33bf5869361576b7aef42733a", size = 250602, upload-time = "2025-08-23T14:41:06.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/2d/548c8e04249cbba3aba6bd799efdd11eee3941b70253733f5d355d689559/coverage-7.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2285c04ee8676f7938b02b4936d9b9b672064daab3187c20f73a55f3d70e6b4a", size = 248724, upload-time = "2025-08-23T14:41:08.429Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/96/a7c3c0562266ac39dcad271d0eec8fc20ab576e3e2f64130a845ad2a557b/coverage-7.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2492e4dd9daab63f5f56286f8a04c51323d237631eb98505d87e4c4ff19ec34", size = 250158, upload-time = "2025-08-23T14:41:09.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/75/74d4be58c70c42ef0b352d597b022baf12dbe2b43e7cb1525f56a0fb1d4b/coverage-7.10.5-cp312-cp312-win32.whl", hash = "sha256:38a9109c4ee8135d5df5505384fc2f20287a47ccbe0b3f04c53c9a1989c2bbaf", size = 219493, upload-time = "2025-08-23T14:41:11.095Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/08/364e6012d1d4d09d1e27437382967efed971d7613f94bca9add25f0c1f2b/coverage-7.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:6b87f1ad60b30bc3c43c66afa7db6b22a3109902e28c5094957626a0143a001f", size = 220302, upload-time = "2025-08-23T14:41:12.449Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/d5/7c8a365e1f7355c58af4fe5faf3f90cc8e587590f5854808d17ccb4e7077/coverage-7.10.5-cp312-cp312-win_arm64.whl", hash = "sha256:672a6c1da5aea6c629819a0e1461e89d244f78d7b60c424ecf4f1f2556c041d8", size = 218936, upload-time = "2025-08-23T14:41:13.872Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/08/4166ecfb60ba011444f38a5a6107814b80c34c717bc7a23be0d22e92ca09/coverage-7.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef3b83594d933020f54cf65ea1f4405d1f4e41a009c46df629dd964fcb6e907c", size = 217106, upload-time = "2025-08-23T14:41:15.268Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/d7/b71022408adbf040a680b8c64bf6ead3be37b553e5844f7465643979f7ca/coverage-7.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b96bfdf7c0ea9faebce088a3ecb2382819da4fbc05c7b80040dbc428df6af44", size = 217353, upload-time = "2025-08-23T14:41:16.656Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/68/21e0d254dbf8972bb8dd95e3fe7038f4be037ff04ba47d6d1b12b37510ba/coverage-7.10.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:63df1fdaffa42d914d5c4d293e838937638bf75c794cf20bee12978fc8c4e3bc", size = 248350, upload-time = "2025-08-23T14:41:18.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/65/28752c3a896566ec93e0219fc4f47ff71bd2b745f51554c93e8dcb659796/coverage-7.10.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8002dc6a049aac0e81ecec97abfb08c01ef0c1fbf962d0c98da3950ace89b869", size = 250955, upload-time = "2025-08-23T14:41:19.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/eb/ca6b7967f57f6fef31da8749ea20417790bb6723593c8cd98a987be20423/coverage-7.10.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63d4bb2966d6f5f705a6b0c6784c8969c468dbc4bcf9d9ded8bff1c7e092451f", size = 252230, upload-time = "2025-08-23T14:41:20.959Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/29/17a411b2a2a18f8b8c952aa01c00f9284a1fbc677c68a0003b772ea89104/coverage-7.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1f672efc0731a6846b157389b6e6d5d5e9e59d1d1a23a5c66a99fd58339914d5", size = 250387, upload-time = "2025-08-23T14:41:22.644Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/89/97a9e271188c2fbb3db82235c33980bcbc733da7da6065afbaa1d685a169/coverage-7.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3f39cef43d08049e8afc1fde4a5da8510fc6be843f8dea350ee46e2a26b2f54c", size = 248280, upload-time = "2025-08-23T14:41:24.061Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/c6/0ad7d0137257553eb4706b4ad6180bec0a1b6a648b092c5bbda48d0e5b2c/coverage-7.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2968647e3ed5a6c019a419264386b013979ff1fb67dd11f5c9886c43d6a31fc2", size = 249894, upload-time = "2025-08-23T14:41:26.165Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/56/fb3aba936addb4c9e5ea14f5979393f1c2466b4c89d10591fd05f2d6b2aa/coverage-7.10.5-cp313-cp313-win32.whl", hash = "sha256:0d511dda38595b2b6934c2b730a1fd57a3635c6aa2a04cb74714cdfdd53846f4", size = 219536, upload-time = "2025-08-23T14:41:27.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/54/baacb8f2f74431e3b175a9a2881feaa8feb6e2f187a0e7e3046f3c7742b2/coverage-7.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:9a86281794a393513cf117177fd39c796b3f8e3759bb2764259a2abba5cce54b", size = 220330, upload-time = "2025-08-23T14:41:29.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/8a/82a3788f8e31dee51d350835b23d480548ea8621f3effd7c3ba3f7e5c006/coverage-7.10.5-cp313-cp313-win_arm64.whl", hash = "sha256:cebd8e906eb98bb09c10d1feed16096700b1198d482267f8bf0474e63a7b8d84", size = 218961, upload-time = "2025-08-23T14:41:30.511Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/a1/590154e6eae07beee3b111cc1f907c30da6fc8ce0a83ef756c72f3c7c748/coverage-7.10.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0520dff502da5e09d0d20781df74d8189ab334a1e40d5bafe2efaa4158e2d9e7", size = 217819, upload-time = "2025-08-23T14:41:31.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/ff/436ffa3cfc7741f0973c5c89405307fe39b78dcf201565b934e6616fc4ad/coverage-7.10.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d9cd64aca68f503ed3f1f18c7c9174cbb797baba02ca8ab5112f9d1c0328cd4b", size = 218040, upload-time = "2025-08-23T14:41:33.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/ca/5787fb3d7820e66273913affe8209c534ca11241eb34ee8c4fd2aaa9dd87/coverage-7.10.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0913dd1613a33b13c4f84aa6e3f4198c1a21ee28ccb4f674985c1f22109f0aae", size = 259374, upload-time = "2025-08-23T14:41:34.914Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/89/21af956843896adc2e64fc075eae3c1cadb97ee0a6960733e65e696f32dd/coverage-7.10.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1b7181c0feeb06ed8a02da02792f42f829a7b29990fef52eff257fef0885d760", size = 261551, upload-time = "2025-08-23T14:41:36.333Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/96/390a69244ab837e0ac137989277879a084c786cf036c3c4a3b9637d43a89/coverage-7.10.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36d42b7396b605f774d4372dd9c49bed71cbabce4ae1ccd074d155709dd8f235", size = 263776, upload-time = "2025-08-23T14:41:38.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/32/cfd6ae1da0a521723349f3129b2455832fc27d3f8882c07e5b6fefdd0da2/coverage-7.10.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b4fdc777e05c4940b297bf47bf7eedd56a39a61dc23ba798e4b830d585486ca5", size = 261326, upload-time = "2025-08-23T14:41:40.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/c4/bf8d459fb4ce2201e9243ce6c015936ad283a668774430a3755f467b39d1/coverage-7.10.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:42144e8e346de44a6f1dbd0a56575dd8ab8dfa7e9007da02ea5b1c30ab33a7db", size = 259090, upload-time = "2025-08-23T14:41:42.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/5d/a234f7409896468e5539d42234016045e4015e857488b0b5b5f3f3fa5f2b/coverage-7.10.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:66c644cbd7aed8fe266d5917e2c9f65458a51cfe5eeff9c05f15b335f697066e", size = 260217, upload-time = "2025-08-23T14:41:43.591Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/ad/87560f036099f46c2ddd235be6476dd5c1d6be6bb57569a9348d43eeecea/coverage-7.10.5-cp313-cp313t-win32.whl", hash = "sha256:2d1b73023854068c44b0c554578a4e1ef1b050ed07cf8b431549e624a29a66ee", size = 220194, upload-time = "2025-08-23T14:41:45.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/a8/04a482594fdd83dc677d4a6c7e2d62135fff5a1573059806b8383fad9071/coverage-7.10.5-cp313-cp313t-win_amd64.whl", hash = "sha256:54a1532c8a642d8cc0bd5a9a51f5a9dcc440294fd06e9dda55e743c5ec1a8f14", size = 221258, upload-time = "2025-08-23T14:41:46.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/ad/7da28594ab66fe2bc720f1bc9b131e62e9b4c6e39f044d9a48d18429cc21/coverage-7.10.5-cp313-cp313t-win_arm64.whl", hash = "sha256:74d5b63fe3f5f5d372253a4ef92492c11a4305f3550631beaa432fc9df16fcff", size = 219521, upload-time = "2025-08-23T14:41:47.882Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/7f/c8b6e4e664b8a95254c35a6c8dd0bf4db201ec681c169aae2f1256e05c85/coverage-7.10.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:68c5e0bc5f44f68053369fa0d94459c84548a77660a5f2561c5e5f1e3bed7031", size = 217090, upload-time = "2025-08-23T14:41:49.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/74/3ee14ede30a6e10a94a104d1d0522d5fb909a7c7cac2643d2a79891ff3b9/coverage-7.10.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cf33134ffae93865e32e1e37df043bef15a5e857d8caebc0099d225c579b0fa3", size = 217365, upload-time = "2025-08-23T14:41:50.796Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/5f/06ac21bf87dfb7620d1f870dfa3c2cae1186ccbcdc50b8b36e27a0d52f50/coverage-7.10.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad8fa9d5193bafcf668231294241302b5e683a0518bf1e33a9a0dfb142ec3031", size = 248413, upload-time = "2025-08-23T14:41:52.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/bc/cc5bed6e985d3a14228539631573f3863be6a2587381e8bc5fdf786377a1/coverage-7.10.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:146fa1531973d38ab4b689bc764592fe6c2f913e7e80a39e7eeafd11f0ef6db2", size = 250943, upload-time = "2025-08-23T14:41:53.922Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/43/6a9fc323c2c75cd80b18d58db4a25dc8487f86dd9070f9592e43e3967363/coverage-7.10.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6013a37b8a4854c478d3219ee8bc2392dea51602dd0803a12d6f6182a0061762", size = 252301, upload-time = "2025-08-23T14:41:56.528Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/7c/3e791b8845f4cd515275743e3775adb86273576596dc9f02dca37357b4f2/coverage-7.10.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:eb90fe20db9c3d930fa2ad7a308207ab5b86bf6a76f54ab6a40be4012d88fcae", size = 250302, upload-time = "2025-08-23T14:41:58.171Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/bc/5099c1e1cb0c9ac6491b281babea6ebbf999d949bf4aa8cdf4f2b53505e8/coverage-7.10.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:384b34482272e960c438703cafe63316dfbea124ac62006a455c8410bf2a2262", size = 248237, upload-time = "2025-08-23T14:41:59.703Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/51/d346eb750a0b2f1e77f391498b753ea906fde69cc11e4b38dca28c10c88c/coverage-7.10.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:467dc74bd0a1a7de2bedf8deaf6811f43602cb532bd34d81ffd6038d6d8abe99", size = 249726, upload-time = "2025-08-23T14:42:01.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/85/eebcaa0edafe427e93286b94f56ea7e1280f2c49da0a776a6f37e04481f9/coverage-7.10.5-cp314-cp314-win32.whl", hash = "sha256:556d23d4e6393ca898b2e63a5bca91e9ac2d5fb13299ec286cd69a09a7187fde", size = 219825, upload-time = "2025-08-23T14:42:03.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/f7/6d43e037820742603f1e855feb23463979bf40bd27d0cde1f761dcc66a3e/coverage-7.10.5-cp314-cp314-win_amd64.whl", hash = "sha256:f4446a9547681533c8fa3e3c6cf62121eeee616e6a92bd9201c6edd91beffe13", size = 220618, upload-time = "2025-08-23T14:42:05.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/b0/ed9432e41424c51509d1da603b0393404b828906236fb87e2c8482a93468/coverage-7.10.5-cp314-cp314-win_arm64.whl", hash = "sha256:5e78bd9cf65da4c303bf663de0d73bf69f81e878bf72a94e9af67137c69b9fe9", size = 219199, upload-time = "2025-08-23T14:42:06.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/54/5a7ecfa77910f22b659c820f67c16fc1e149ed132ad7117f0364679a8fa9/coverage-7.10.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5661bf987d91ec756a47c7e5df4fbcb949f39e32f9334ccd3f43233bbb65e508", size = 217833, upload-time = "2025-08-23T14:42:08.262Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/0e/25672d917cc57857d40edf38f0b867fb9627115294e4f92c8fcbbc18598d/coverage-7.10.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a46473129244db42a720439a26984f8c6f834762fc4573616c1f37f13994b357", size = 218048, upload-time = "2025-08-23T14:42:10.247Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/7c/0b2b4f1c6f71885d4d4b2b8608dcfc79057adb7da4143eb17d6260389e42/coverage-7.10.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1f64b8d3415d60f24b058b58d859e9512624bdfa57a2d1f8aff93c1ec45c429b", size = 259549, upload-time = "2025-08-23T14:42:11.811Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/73/abb8dab1609abec7308d83c6aec547944070526578ee6c833d2da9a0ad42/coverage-7.10.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:44d43de99a9d90b20e0163f9770542357f58860a26e24dc1d924643bd6aa7cb4", size = 261715, upload-time = "2025-08-23T14:42:13.505Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/d1/abf31de21ec92731445606b8d5e6fa5144653c2788758fcf1f47adb7159a/coverage-7.10.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a931a87e5ddb6b6404e65443b742cb1c14959622777f2a4efd81fba84f5d91ba", size = 263969, upload-time = "2025-08-23T14:42:15.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/b3/ef274927f4ebede96056173b620db649cc9cb746c61ffc467946b9d0bc67/coverage-7.10.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9559b906a100029274448f4c8b8b0a127daa4dade5661dfd821b8c188058842", size = 261408, upload-time = "2025-08-23T14:42:16.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/fc/83ca2812be616d69b4cdd4e0c62a7bc526d56875e68fd0f79d47c7923584/coverage-7.10.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b08801e25e3b4526ef9ced1aa29344131a8f5213c60c03c18fe4c6170ffa2874", size = 259168, upload-time = "2025-08-23T14:42:18.512Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/4f/e0779e5716f72d5c9962e709d09815d02b3b54724e38567308304c3fc9df/coverage-7.10.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ed9749bb8eda35f8b636fb7632f1c62f735a236a5d4edadd8bbcc5ea0542e732", size = 260317, upload-time = "2025-08-23T14:42:20.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/fe/4247e732f2234bb5eb9984a0888a70980d681f03cbf433ba7b48f08ca5d5/coverage-7.10.5-cp314-cp314t-win32.whl", hash = "sha256:609b60d123fc2cc63ccee6d17e4676699075db72d14ac3c107cc4976d516f2df", size = 220600, upload-time = "2025-08-23T14:42:22.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/a0/f294cff6d1034b87839987e5b6ac7385bec599c44d08e0857ac7f164ad0c/coverage-7.10.5-cp314-cp314t-win_amd64.whl", hash = "sha256:0666cf3d2c1626b5a3463fd5b05f5e21f99e6aec40a3192eee4d07a15970b07f", size = 221714, upload-time = "2025-08-23T14:42:23.616Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/18/fa1afdc60b5528d17416df440bcbd8fd12da12bfea9da5b6ae0f7a37d0f7/coverage-7.10.5-cp314-cp314t-win_arm64.whl", hash = "sha256:bc85eb2d35e760120540afddd3044a5bf69118a91a296a8b3940dfc4fdcfe1e2", size = 219735, upload-time = "2025-08-23T14:42:25.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/21/05248e8bc74683488cb7477e6b6b878decadd15af0ec96f56381d3d7ff2d/coverage-7.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:62835c1b00c4a4ace24c1a88561a5a59b612fbb83a525d1c70ff5720c97c0610", size = 216763, upload-time = "2025-08-23T14:42:26.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/7f/161a0ad40cb1c7e19dc1aae106d3430cc88dac3d651796d6cf3f3730c800/coverage-7.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5255b3bbcc1d32a4069d6403820ac8e6dbcc1d68cb28a60a1ebf17e47028e898", size = 217154, upload-time = "2025-08-23T14:42:28.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/31/41929ee53af829ea5a88e71d335ea09d0bb587a3da1c5e58e59b48473ed8/coverage-7.10.5-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3876385722e335d6e991c430302c24251ef9c2a9701b2b390f5473199b1b8ebf", size = 243588, upload-time = "2025-08-23T14:42:29.798Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/4e/2649344e33eeb3567041e8255a1942173cae81817fe06b60f3fafaafe111/coverage-7.10.5-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8048ce4b149c93447a55d279078c8ae98b08a6951a3c4d2d7e87f4efc7bfe100", size = 245412, upload-time = "2025-08-23T14:42:31.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/b1/b21e1e69986ad89b051dd42c3ef06d9326e03ac3c0c844fc33385d1d9e35/coverage-7.10.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4028e7558e268dd8bcf4d9484aad393cafa654c24b4885f6f9474bf53183a82a", size = 247182, upload-time = "2025-08-23T14:42:33.155Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/b5/80837be411ae092e03fcc2a7877bd9a659c531eff50453e463057a9eee44/coverage-7.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03f47dc870eec0367fcdd603ca6a01517d2504e83dc18dbfafae37faec66129a", size = 245066, upload-time = "2025-08-23T14:42:34.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/ed/fcb0838ddf149d68d09f89af57397b0dd9d26b100cc729daf1b0caf0b2d3/coverage-7.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2d488d7d42b6ded7ea0704884f89dcabd2619505457de8fc9a6011c62106f6e5", size = 243138, upload-time = "2025-08-23T14:42:36.311Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/0f/505c6af24a9ae5d8919d209b9c31b7092815f468fa43bec3b1118232c62a/coverage-7.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b3dcf2ead47fa8be14224ee817dfc1df98043af568fe120a22f81c0eb3c34ad2", size = 244095, upload-time = "2025-08-23T14:42:38.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/7e/c82a8bede46217c1d944bd19b65e7106633b998640f00ab49c5f747a5844/coverage-7.10.5-cp39-cp39-win32.whl", hash = "sha256:02650a11324b80057b8c9c29487020073d5e98a498f1857f37e3f9b6ea1b2426", size = 219289, upload-time = "2025-08-23T14:42:39.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/ac/46645ef6be543f2e7de08cc2601a0b67e130c816be3b749ab741be689fb9/coverage-7.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:b45264dd450a10f9e03237b41a9a24e85cbb1e278e5a32adb1a303f58f0017f3", size = 220199, upload-time = "2025-08-23T14:42:41.363Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/b6/fff6609354deba9aeec466e4bcaeb9d1ed3e5d60b14b57df2a36fb2273f2/coverage-7.10.5-py3-none-any.whl", hash = "sha256:0be24d35e4db1d23d0db5c0f6a74a962e2ec83c426b5cac09f4234aadef38e4a", size = 208736, upload-time = "2025-08-23T14:42:43.145Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -1145,7 +1145,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "jupyter-server"
|
||||
version = "2.16.0"
|
||||
version = "2.17.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
@@ -1157,7 +1157,7 @@ dependencies = [
|
||||
{ name = "jupyter-server-terminals" },
|
||||
{ name = "nbconvert" },
|
||||
{ name = "nbformat" },
|
||||
{ name = "overrides" },
|
||||
{ name = "overrides", marker = "python_full_version < '3.12'" },
|
||||
{ name = "packaging" },
|
||||
{ name = "prometheus-client" },
|
||||
{ name = "pywinpty", marker = "os_name == 'nt'" },
|
||||
@@ -1168,9 +1168,9 @@ dependencies = [
|
||||
{ name = "traitlets" },
|
||||
{ name = "websocket-client" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/41/c8/ba2bbcd758c47f1124c4ca14061e8ce60d9c6fd537faee9534a95f83521a/jupyter_server-2.16.0.tar.gz", hash = "sha256:65d4b44fdf2dcbbdfe0aa1ace4a842d4aaf746a2b7b168134d5aaed35621b7f6", size = 728177, upload-time = "2025-05-12T16:44:46.245Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949, upload-time = "2025-08-21T14:42:54.042Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/46/1f/5ebbced977171d09a7b0c08a285ff9a20aafb9c51bde07e52349ff1ddd71/jupyter_server-2.16.0-py3-none-any.whl", hash = "sha256:3d8db5be3bc64403b1c65b400a1d7f4647a5ce743f3b20dbdefe8ddb7b55af9e", size = 386904, upload-time = "2025-05-12T16:44:43.335Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221, upload-time = "2025-08-21T14:42:52.034Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1269,7 +1269,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -1350,7 +1350,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-api"
|
||||
version = "0.2.135"
|
||||
version = "0.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cloudpickle", marker = "python_full_version >= '3.11'" },
|
||||
@@ -1373,9 +1373,9 @@ dependencies = [
|
||||
{ name = "uvicorn", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "watchfiles", marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8f/1e/5ca12f00852e2f54075c65dc2d4eca4d2d8facd404d5c0d17803fe620eae/langgraph_api-0.2.135.tar.gz", hash = "sha256:8cbcd5ff7be89a72d01dd3b32b0873bc5114ab9ff206fc94c2781ba56d09182a", size = 255563, upload-time = "2025-08-20T01:02:01.661Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/c5/1bcaf07f3f973a4a540bd341f20dfc60f0fc3cac8e7c2dc606680cb1d11e/langgraph_api-0.4.1.tar.gz", hash = "sha256:6f12b8be914ad9a9f11e3b73278fc90aeb7b4988b0418afbdf686c6fd5577c74", size = 1579764, upload-time = "2025-08-26T12:23:48.172Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/98/18c475de127927622079e087b7cfe1ab97ed44fe1a2425f8c8de07ea0e64/langgraph_api-0.2.135-py3-none-any.whl", hash = "sha256:ac78035075230ccb816f48b242a76c5f8e607c074fe7802928425ba76a11b3f4", size = 205849, upload-time = "2025-08-20T01:02:00.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/5e/b36e6035b52e06a9c0bd04030255a03b2444c9d2cad347b96193674cf4f5/langgraph_api-0.4.1-py3-none-any.whl", hash = "sha256:1f5f8d33bca30200e954e81416f4de26bce173a7f2e623b1e509d1b8a44b49a3", size = 1536988, upload-time = "2025-08-26T12:23:45.834Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1491,8 +1491,8 @@ inmem = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "click", specifier = ">=8.1.7" },
|
||||
{ name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.120,<0.4.0" },
|
||||
{ name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.6.8" },
|
||||
{ name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3,<0.5.0" },
|
||||
{ name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.7" },
|
||||
{ name = "langgraph-sdk", marker = "python_full_version >= '3.11'", specifier = ">=0.1.0" },
|
||||
{ name = "python-dotenv", marker = "extra == 'inmem'", specifier = ">=0.8.0" },
|
||||
]
|
||||
@@ -1543,7 +1543,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-runtime-inmem"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "blockbuster", marker = "python_full_version >= '3.11'" },
|
||||
@@ -1553,9 +1553,9 @@ dependencies = [
|
||||
{ name = "starlette", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "structlog", marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/94/02b58b1c137cfca6507f6c495e5794fd542b1f5269ef89fb662799be4b8c/langgraph_runtime_inmem-0.8.0.tar.gz", hash = "sha256:3082273f65650665b4a3875241721087fd51e675eb0227b18dc271839ce99594", size = 79510, upload-time = "2025-08-18T09:00:09.162Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2d/88/e5a0a776b2db76c114e3dc67eaa79135f008cc23c1af0802069acd9272d6/langgraph_runtime_inmem-0.9.0.tar.gz", hash = "sha256:7c5adfc98a5cce77c9d8413f606ce6edce315d15e4617a1191e066a0afe38802", size = 81401, upload-time = "2025-08-26T01:02:10.709Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/0e/b09c1aff0bcfdb1357be212b4a5d55f5b98644eae7fab6b115aa463e99fa/langgraph_runtime_inmem-0.8.0-py3-none-any.whl", hash = "sha256:85398321fc186618b0957c4d8629cc059fce2e7f57a4756ef83a75575791da1b", size = 31626, upload-time = "2025-08-18T09:00:08.256Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/e5/8da2e1cc9a97750a346b21b81fc52d2d4127c226775a3071436d6c67e97b/langgraph_runtime_inmem-0.9.0-py3-none-any.whl", hash = "sha256:541e3666d37038f15fa6c2fe34667ae649fd79913de50720241f4017f8b7b946", size = 33479, upload-time = "2025-08-26T01:02:09.522Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1585,7 +1585,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langsmith"
|
||||
version = "0.4.14"
|
||||
version = "0.4.17"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httpx" },
|
||||
@@ -1596,9 +1596,9 @@ dependencies = [
|
||||
{ name = "requests-toolbelt" },
|
||||
{ name = "zstandard" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/b0/1def3c6d12eb5e412213e39f1ba4ac64a47ec3102cf42a3a1ff86af1402d/langsmith-0.4.14.tar.gz", hash = "sha256:4d29c7a9c85b20ba813ab9c855407bccdf5eb4f397f512ffa89959b2a2cb83ed", size = 921872, upload-time = "2025-08-12T20:39:43.704Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/6e/6a76eed14cbf43db79030f8e348de2fdad904334967df34d1c162641679c/langsmith-0.4.17.tar.gz", hash = "sha256:c91e218cd96ee9a310f4685ea7f2b958a54aec919f0f368e694f42431e32c422", size = 931257, upload-time = "2025-08-26T01:42:47.394Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/08/3f0fb3e2f7cc6fd91c4d06d7abc6607425a66973bee79d04018bac41dd4f/langsmith-0.4.14-py3-none-any.whl", hash = "sha256:b6d070ac425196947d2a98126fb0e35f3b8c001a2e6e5b7049dd1c56f0767d0b", size = 373249, upload-time = "2025-08-12T20:39:41.992Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/4c/6f9106237cbeb193e3138393f7134310a38e0db9e6acfc1f43567dfa0559/langsmith-0.4.17-py3-none-any.whl", hash = "sha256:b824ef6a3a0669faf2bc61799e22af0b05a3a53d12cb9a448f611f861a69e991", size = 376351, upload-time = "2025-08-26T01:42:45.101Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2022,11 +2022,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "parso"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609, upload-time = "2024-04-05T09:43:55.897Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650, upload-time = "2024-04-05T09:43:53.299Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2659,90 +2659,90 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyzmq"
|
||||
version = "27.0.1"
|
||||
version = "27.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "implementation_name == 'pypy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/30/5f/557d2032a2f471edbcc227da724c24a1c05887b5cda1e3ae53af98b9e0a5/pyzmq-27.0.1.tar.gz", hash = "sha256:45c549204bc20e7484ffd2555f6cf02e572440ecf2f3bdd60d4404b20fddf64b", size = 281158, upload-time = "2025-08-03T05:05:40.352Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f8/66/159f38d184f08b5f971b467f87b1ab142ab1320d5200825c824b32b84b66/pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798", size = 281440, upload-time = "2025-08-21T04:23:26.334Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/72/0b/ccf4d0b152a6a11f0fc01e73978202fe0e8fe0e91e20941598e83a170bee/pyzmq-27.0.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:90a4da42aa322de8a3522461e3b5fe999935763b27f69a02fced40f4e3cf9682", size = 1329293, upload-time = "2025-08-03T05:02:56.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/76/48706d291951b1300d3cf985e503806901164bf1581f27c4b6b22dbab2fa/pyzmq-27.0.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e648dca28178fc879c814cf285048dd22fd1f03e1104101106505ec0eea50a4d", size = 905953, upload-time = "2025-08-03T05:02:59.061Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/8a/df3135b96712068d184c53120c7dbf3023e5e362a113059a4f85cd36c6a0/pyzmq-27.0.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bca8abc31799a6f3652d13f47e0b0e1cab76f9125f2283d085a3754f669b607", size = 666165, upload-time = "2025-08-03T05:03:00.789Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/ed/341a7148e08d2830f480f53ab3d136d88fc5011bb367b516d95d0ebb46dd/pyzmq-27.0.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:092f4011b26d6b0201002f439bd74b38f23f3aefcb358621bdc3b230afc9b2d5", size = 853756, upload-time = "2025-08-03T05:03:03.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/bc/d26fe010477c3e901f0f5a3e70446950dde9aa217f1d1a13534eb0fccfe5/pyzmq-27.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f02f30a4a6b3efe665ab13a3dd47109d80326c8fd286311d1ba9f397dc5f247", size = 1654870, upload-time = "2025-08-03T05:03:05.331Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/21/9b488086bf3f55b2eb26db09007a3962f62f3b81c5c6295a6ff6aaebd69c/pyzmq-27.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f293a1419266e3bf3557d1f8778f9e1ffe7e6b2c8df5c9dca191caf60831eb74", size = 2033444, upload-time = "2025-08-03T05:03:07.318Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/53/85b64a792223cd43393d25e03c8609df41aac817ea5ce6a27eceeed433ee/pyzmq-27.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ce181dd1a7c6c012d0efa8ab603c34b5ee9d86e570c03415bbb1b8772eeb381c", size = 1891289, upload-time = "2025-08-03T05:03:08.96Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/5b/078aae8fe1c4cdba1a77a598870c548fd52b4d4a11e86b8116bbef47d9f3/pyzmq-27.0.1-cp310-cp310-win32.whl", hash = "sha256:f65741cc06630652e82aa68ddef4986a3ab9073dd46d59f94ce5f005fa72037c", size = 566693, upload-time = "2025-08-03T05:03:10.711Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/e1/4471fff36416ebf1ffe43577b9c7dcf2ff4798f2171f0d169640a48d2305/pyzmq-27.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:44909aa3ed2234d69fe81e1dade7be336bcfeab106e16bdaa3318dcde4262b93", size = 631649, upload-time = "2025-08-03T05:03:12.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/4c/8edac8dd56f223124aa40403d2c097bbad9b0e2868a67cad9a2a029863aa/pyzmq-27.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:4401649bfa0a38f0f8777f8faba7cd7eb7b5b8ae2abc7542b830dd09ad4aed0d", size = 559274, upload-time = "2025-08-03T05:03:13.728Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/18/a8e0da6ababbe9326116fb1c890bf1920eea880e8da621afb6bc0f39a262/pyzmq-27.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:9729190bd770314f5fbba42476abf6abe79a746eeda11d1d68fd56dd70e5c296", size = 1332721, upload-time = "2025-08-03T05:03:15.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/a4/9431ba598651d60ebd50dc25755402b770322cf8432adcc07d2906e53a54/pyzmq-27.0.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:696900ef6bc20bef6a242973943574f96c3f97d2183c1bd3da5eea4f559631b1", size = 908249, upload-time = "2025-08-03T05:03:16.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/7a/e624e1793689e4e685d2ee21c40277dd4024d9d730af20446d88f69be838/pyzmq-27.0.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96a63aecec22d3f7fdea3c6c98df9e42973f5856bb6812c3d8d78c262fee808", size = 668649, upload-time = "2025-08-03T05:03:18.49Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/29/0652a39d4e876e0d61379047ecf7752685414ad2e253434348246f7a2a39/pyzmq-27.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c512824360ea7490390566ce00bee880e19b526b312b25cc0bc30a0fe95cb67f", size = 856601, upload-time = "2025-08-03T05:03:20.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/2d/8d5355d7fc55bb6e9c581dd74f58b64fa78c994079e3a0ea09b1b5627cde/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dfb2bb5e0f7198eaacfb6796fb0330afd28f36d985a770745fba554a5903595a", size = 1657750, upload-time = "2025-08-03T05:03:22.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/f4/cd032352d5d252dc6f5ee272a34b59718ba3af1639a8a4ef4654f9535cf5/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4f6886c59ba93ffde09b957d3e857e7950c8fe818bd5494d9b4287bc6d5bc7f1", size = 2034312, upload-time = "2025-08-03T05:03:23.578Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/1a/c050d8b6597200e97a4bd29b93c769d002fa0b03083858227e0376ad59bc/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b99ea9d330e86ce1ff7f2456b33f1bf81c43862a5590faf4ef4ed3a63504bdab", size = 1893632, upload-time = "2025-08-03T05:03:25.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/29/173ce21d5097e7fcf284a090e8beb64fc683c6582b1f00fa52b1b7e867ce/pyzmq-27.0.1-cp311-cp311-win32.whl", hash = "sha256:571f762aed89025ba8cdcbe355fea56889715ec06d0264fd8b6a3f3fa38154ed", size = 566587, upload-time = "2025-08-03T05:03:26.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/ab/22bd33e7086f0a2cc03a5adabff4bde414288bb62a21a7820951ef86ec20/pyzmq-27.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee16906c8025fa464bea1e48128c048d02359fb40bebe5333103228528506530", size = 632873, upload-time = "2025-08-03T05:03:28.685Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/14/3e59b4a28194285ceeff725eba9aa5ba8568d1cb78aed381dec1537c705a/pyzmq-27.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:ba068f28028849da725ff9185c24f832ccf9207a40f9b28ac46ab7c04994bd41", size = 558918, upload-time = "2025-08-03T05:03:30.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/9b/c0957041067c7724b310f22c398be46399297c12ed834c3bc42200a2756f/pyzmq-27.0.1-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:af7ebce2a1e7caf30c0bb64a845f63a69e76a2fadbc1cac47178f7bb6e657bdd", size = 1305432, upload-time = "2025-08-03T05:03:32.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/55/bd3a312790858f16b7def3897a0c3eb1804e974711bf7b9dcb5f47e7f82c/pyzmq-27.0.1-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8f617f60a8b609a13099b313e7e525e67f84ef4524b6acad396d9ff153f6e4cd", size = 895095, upload-time = "2025-08-03T05:03:33.918Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/50/fc384631d8282809fb1029a4460d2fe90fa0370a0e866a8318ed75c8d3bb/pyzmq-27.0.1-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d59dad4173dc2a111f03e59315c7bd6e73da1a9d20a84a25cf08325b0582b1a", size = 651826, upload-time = "2025-08-03T05:03:35.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/0a/2356305c423a975000867de56888b79e44ec2192c690ff93c3109fd78081/pyzmq-27.0.1-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5b6133c8d313bde8bd0d123c169d22525300ff164c2189f849de495e1344577", size = 839751, upload-time = "2025-08-03T05:03:37.265Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/1b/81e95ad256ca7e7ccd47f5294c1c6da6e2b64fbace65b84fe8a41470342e/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:58cca552567423f04d06a075f4b473e78ab5bdb906febe56bf4797633f54aa4e", size = 1641359, upload-time = "2025-08-03T05:03:38.799Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/63/9f50ec965285f4e92c265c8f18344e46b12803666d8b73b65d254d441435/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:4b9d8e26fb600d0d69cc9933e20af08552e97cc868a183d38a5c0d661e40dfbb", size = 2020281, upload-time = "2025-08-03T05:03:40.338Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/4a/19e3398d0dc66ad2b463e4afa1fc541d697d7bc090305f9dfb948d3dfa29/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2329f0c87f0466dce45bba32b63f47018dda5ca40a0085cc5c8558fea7d9fc55", size = 1877112, upload-time = "2025-08-03T05:03:42.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/42/c562e9151aa90ed1d70aac381ea22a929d6b3a2ce4e1d6e2e135d34fd9c6/pyzmq-27.0.1-cp312-abi3-win32.whl", hash = "sha256:57bb92abdb48467b89c2d21da1ab01a07d0745e536d62afd2e30d5acbd0092eb", size = 558177, upload-time = "2025-08-03T05:03:43.979Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/96/5c50a7d2d2b05b19994bf7336b97db254299353dd9b49b565bb71b485f03/pyzmq-27.0.1-cp312-abi3-win_amd64.whl", hash = "sha256:ff3f8757570e45da7a5bedaa140489846510014f7a9d5ee9301c61f3f1b8a686", size = 618923, upload-time = "2025-08-03T05:03:45.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/33/1ec89c8f21c89d21a2eaff7def3676e21d8248d2675705e72554fb5a6f3f/pyzmq-27.0.1-cp312-abi3-win_arm64.whl", hash = "sha256:df2c55c958d3766bdb3e9d858b911288acec09a9aab15883f384fc7180df5bed", size = 552358, upload-time = "2025-08-03T05:03:46.887Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/a0/f26e276211ec8090a4d11e4ec70eb8a8b15781e591c1d44ce62f372963a0/pyzmq-27.0.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:497bd8af534ae55dc4ef67eebd1c149ff2a0b0f1e146db73c8b5a53d83c1a5f5", size = 1122287, upload-time = "2025-08-03T05:03:48.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/d8/af4b507e4f7eeea478cc8ee873995a6fd55582bfb99140593ed460e1db3c/pyzmq-27.0.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:a066ea6ad6218b4c233906adf0ae67830f451ed238419c0db609310dd781fbe7", size = 1155756, upload-time = "2025-08-03T05:03:50.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/55/37fae0013e11f88681da42698e550b08a316d608242551f65095cc99232a/pyzmq-27.0.1-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:72d235d6365ca73d8ce92f7425065d70f5c1e19baa458eb3f0d570e425b73a96", size = 1340826, upload-time = "2025-08-03T05:03:52.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/e4/3a87854c64b26fcf63a9d1b6f4382bd727d4797c772ceb334a97b7489be9/pyzmq-27.0.1-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:313a7b374e3dc64848644ca348a51004b41726f768b02e17e689f1322366a4d9", size = 897283, upload-time = "2025-08-03T05:03:54.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/3e/4296c6b0ad2d07be11ae1395dccf9cae48a0a655cf9be1c3733ad2b591d1/pyzmq-27.0.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:119ce8590409702394f959c159d048002cbed2f3c0645ec9d6a88087fc70f0f1", size = 660565, upload-time = "2025-08-03T05:03:56.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/41/a33ba3aa48b45b23c4cd4ac49aafde46f3e0f81939f2bfb3b6171a437122/pyzmq-27.0.1-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45c3e00ce16896ace2cd770ab9057a7cf97d4613ea5f2a13f815141d8b6894b9", size = 847680, upload-time = "2025-08-03T05:03:57.696Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/8c/bf2350bb25b3b58d2e5b5d2290ffab0e923f0cc6d02288d3fbf4baa6e4d1/pyzmq-27.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:678e50ec112bdc6df5a83ac259a55a4ba97a8b314c325ab26b3b5b071151bc61", size = 1650151, upload-time = "2025-08-03T05:03:59.387Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/1a/a5a07c54890891344a8ddc3d5ab320dd3c4e39febb6e4472546e456d5157/pyzmq-27.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d0b96c30be9f9387b18b18b6133c75a7b1b0065da64e150fe1feb5ebf31ece1c", size = 2023766, upload-time = "2025-08-03T05:04:01.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/5e/514dcff08f02c6c8a45a6e23621901139cf853be7ac5ccd0b9407c3aa3de/pyzmq-27.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88dc92d9eb5ea4968123e74db146d770b0c8d48f0e2bfb1dbc6c50a8edb12d64", size = 1885195, upload-time = "2025-08-03T05:04:03.923Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/91/87f74f98a487fbef0b115f6025e4a295129fd56b2b633a03ba7d5816ecc2/pyzmq-27.0.1-cp313-cp313t-win32.whl", hash = "sha256:6dcbcb34f5c9b0cefdfc71ff745459241b7d3cda5b27c7ad69d45afc0821d1e1", size = 574213, upload-time = "2025-08-03T05:04:05.905Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/d7/07f7d0d7f4c81e08be7b60e52ff2591c557377c017f96204d33d5fca1b07/pyzmq-27.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9fd0fda730461f510cfd9a40fafa5355d65f5e3dbdd8d6dfa342b5b3f5d1949", size = 640202, upload-time = "2025-08-03T05:04:07.439Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/83/21d66bcef6fb803647a223cbde95111b099e2176277c0cbc8b099c485510/pyzmq-27.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:56a3b1853f3954ec1f0e91085f1350cc57d18f11205e4ab6e83e4b7c414120e0", size = 561514, upload-time = "2025-08-03T05:04:09.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/0b/d5ea75cf46b52cdce85a85200c963cb498932953df443892238be49b1a01/pyzmq-27.0.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:f98f6b7787bd2beb1f0dde03f23a0621a0c978edf673b7d8f5e7bc039cbe1b60", size = 1340836, upload-time = "2025-08-03T05:04:10.774Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/4c/0dbce882550e17db6846b29e9dc242aea7590e7594e1ca5043e8e58fff2d/pyzmq-27.0.1-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:351bf5d8ca0788ca85327fda45843b6927593ff4c807faee368cc5aaf9f809c2", size = 897236, upload-time = "2025-08-03T05:04:13.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/22/461e131cf16b8814f3c356fa1ea0912697dbc4c64cddf01f7756ec704c1e/pyzmq-27.0.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5268a5a9177afff53dc6d70dffe63114ba2a6e7b20d9411cc3adeba09eeda403", size = 660374, upload-time = "2025-08-03T05:04:15.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/0c/bbd65a814395bf4fc3e57c6c13af27601c07e4009bdfb75ebcf500537bbd/pyzmq-27.0.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4aca06ba295aa78bec9b33ec028d1ca08744c36294338c41432b7171060c808", size = 847497, upload-time = "2025-08-03T05:04:16.967Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/df/3d1f4a03b561d824cbd491394f67591957e2f1acf6dc85d96f970312a76a/pyzmq-27.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1c363c6dc66352331d5ad64bb838765c6692766334a6a02fdb05e76bd408ae18", size = 1650028, upload-time = "2025-08-03T05:04:19.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/c9/a3987540f59a412bdaae3f362f78e00e6769557a598c63b7e32956aade5a/pyzmq-27.0.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:87aebf4acd7249bdff8d3df03aed4f09e67078e6762cfe0aecf8d0748ff94cde", size = 2023808, upload-time = "2025-08-03T05:04:21.145Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/a5/c388f4cd80498a8eaef7535f2a8eaca0a35b82b87a0b47fa1856fc135004/pyzmq-27.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e4f22d67756518d71901edf73b38dc0eb4765cce22c8fe122cc81748d425262b", size = 1884970, upload-time = "2025-08-03T05:04:22.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/ac/b2a89a1ed90526a1b9a260cdc5cd42f055fd44ee8d2a59902b5ac35ddeb1/pyzmq-27.0.1-cp314-cp314t-win32.whl", hash = "sha256:8c62297bc7aea2147b472ca5ca2b4389377ad82898c87cabab2a94aedd75e337", size = 586905, upload-time = "2025-08-03T05:04:24.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/62/7aa5ea04e836f7a788b2a67405f83011cef59ca76d7bac91d1fc9a0476da/pyzmq-27.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:bee5248d5ec9223545f8cc4f368c2d571477ae828c99409125c3911511d98245", size = 660503, upload-time = "2025-08-03T05:04:26.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/32/3836ed85947b06f1d67c07ce16c00b0cf8c053ab0b249d234f9f81ff95ff/pyzmq-27.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:0fc24bf45e4a454e55ef99d7f5c8b8712539200ce98533af25a5bfa954b6b390", size = 575098, upload-time = "2025-08-03T05:04:27.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/f1/cdceaf9b6637570f36eee2dbd25bc5a800637cd9b4103b15fbc4b0658b82/pyzmq-27.0.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:05a94233fdde585eb70924a6e4929202a747eea6ed308a6171c4f1c715bbe39e", size = 1330651, upload-time = "2025-08-03T05:04:45.583Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/5c/469d3b9315eb4d5c61c431a4ae8acdb6abb165dfa5ddbc7af639be53891c/pyzmq-27.0.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c96702e1082eab62ae583d64c4e19c9b848359196697e536a0c57ae9bd165bd5", size = 906524, upload-time = "2025-08-03T05:04:47.904Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/c0/c7a12a533a87beb1143f4a9c8f4d6f82775c04eb3ad27f664e0ef00a6189/pyzmq-27.0.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c9180d1f5b4b73e28b64e63cc6c4c097690f102aa14935a62d5dd7426a4e5b5a", size = 863547, upload-time = "2025-08-03T05:04:49.579Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/78/50907d004511bd23eae03d951f3ca4e4cc2e7eb5ec8d3df70d89eca3f97c/pyzmq-27.0.1-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e971d8680003d0af6020713e52f92109b46fedb463916e988814e04c8133578a", size = 666797, upload-time = "2025-08-03T05:04:51.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/bd/ec3388888eda39705a4cefb465452a4bca5430a3435803588ced49943fdb/pyzmq-27.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe632fa4501154d58dfbe1764a0495734d55f84eaf1feda4549a1f1ca76659e9", size = 1655601, upload-time = "2025-08-03T05:04:53.026Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/50/170a1671a171365dda677886d42c39629a086752696ede70296b8f6224d8/pyzmq-27.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4c3874344fd5fa6d58bb51919708048ac4cab21099f40a227173cddb76b4c20b", size = 2034120, upload-time = "2025-08-03T05:04:55.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/0a/f06841495e4ec33ed65588e94aff07f1dcbc6878e1611577f6b97a449068/pyzmq-27.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ec09073ed67ae236785d543df3b322282acc0bdf6d1b748c3e81f3043b21cb5", size = 1891956, upload-time = "2025-08-03T05:04:57.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/6b/6ba945a4756e4b1ba69b909d2b040d16aff0f0edd56a60874970b8d47237/pyzmq-27.0.1-cp39-cp39-win32.whl", hash = "sha256:f44e7ea288d022d4bf93b9e79dafcb4a7aea45a3cbeae2116792904931cefccf", size = 567388, upload-time = "2025-08-03T05:04:58.704Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/b4/8ffb9cfb363bc9d61c5d8d9f79a7ada572b0865dac9f4a547da901b81d76/pyzmq-27.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ffe6b809a97ac6dea524b3b837d5b28743d8c2f121141056d168ff0ba8f614ef", size = 632004, upload-time = "2025-08-03T05:05:00.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/4b/dd5c4d3bb7261efb30a909d2df447ac77393653e5c34c8a9cd536f429c3e/pyzmq-27.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:fde26267416c8478c95432c81489b53f57b0b5d24cd5c8bfaebf5bbaac4dc90c", size = 559881, upload-time = "2025-08-03T05:05:02.363Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/87/fc96f224dd99070fe55d0afc37ac08d7d4635d434e3f9425b232867e01b9/pyzmq-27.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:544b995a6a1976fad5d7ff01409b4588f7608ccc41be72147700af91fd44875d", size = 835950, upload-time = "2025-08-03T05:05:04.193Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/b6/802d96017f176c3a7285603d9ed2982550095c136c6230d3e0b53f52c7e5/pyzmq-27.0.1-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0f772eea55cccce7f45d6ecdd1d5049c12a77ec22404f6b892fae687faa87bee", size = 799876, upload-time = "2025-08-03T05:05:06.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/52/49045c6528007cce385f218f3a674dc84fc8b3265330d09e57c0a59b41f4/pyzmq-27.0.1-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9d63d66059114a6756d09169c9209ffceabacb65b9cb0f66e6fc344b20b73e6", size = 567402, upload-time = "2025-08-03T05:05:08.028Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/fe/c29ac0d5a817543ecf0cb18f17195805bad0da567a1c64644aacf11b2779/pyzmq-27.0.1-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1da8e645c655d86f0305fb4c65a0d848f461cd90ee07d21f254667287b5dbe50", size = 747030, upload-time = "2025-08-03T05:05:10.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/d1/cc1fbfb65b4042016e4e035b2548cdfe0945c817345df83aa2d98490e7fc/pyzmq-27.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1843fd0daebcf843fe6d4da53b8bdd3fc906ad3e97d25f51c3fed44436d82a49", size = 544567, upload-time = "2025-08-03T05:05:11.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/1a/49f66fe0bc2b2568dd4280f1f520ac8fafd73f8d762140e278d48aeaf7b9/pyzmq-27.0.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7fb0ee35845bef1e8c4a152d766242164e138c239e3182f558ae15cb4a891f94", size = 835949, upload-time = "2025-08-03T05:05:13.798Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/94/443c1984b397eab59b14dd7ae8bc2ac7e8f32dbc646474453afcaa6508c4/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f379f11e138dfd56c3f24a04164f871a08281194dd9ddf656a278d7d080c8ad0", size = 799875, upload-time = "2025-08-03T05:05:15.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/f1/fd96138a0f152786a2ba517e9c6a8b1b3516719e412a90bb5d8eea6b660c/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b978c0678cffbe8860ec9edc91200e895c29ae1ac8a7085f947f8e8864c489fb", size = 567403, upload-time = "2025-08-03T05:05:17.326Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/57/34e53ef2b55b1428dac5aabe3a974a16c8bda3bf20549ba500e3ff6cb426/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ebccf0d760bc92a4a7c751aeb2fef6626144aace76ee8f5a63abeb100cae87f", size = 747032, upload-time = "2025-08-03T05:05:19.074Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/b7/769598c5ae336fdb657946950465569cf18803140fe89ce466d7f0a57c11/pyzmq-27.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:77fed80e30fa65708546c4119840a46691290efc231f6bfb2ac2a39b52e15811", size = 544566, upload-time = "2025-08-03T05:05:20.798Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/8d/c0880acd2d5908eec6fe9b399f0fb630e5f203f8a69f82442d5cb2b2f46c/pyzmq-27.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d97b59cbd8a6c8b23524a8ce237ff9504d987dc07156258aa68ae06d2dd5f34d", size = 835946, upload-time = "2025-08-03T05:05:31.161Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/35/6b71409aa6629b3d4917b38961501898827f4fb5ddc680cc8e0cb13987f3/pyzmq-27.0.1-pp39-pypy39_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:27a78bdd384dbbe7b357af95f72efe8c494306b5ec0a03c31e2d53d6763e5307", size = 799870, upload-time = "2025-08-03T05:05:33.01Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/f6/5d36d8f6571478f32c32f5872abd76eda052746283ca87e24cc5758f7987/pyzmq-27.0.1-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b007e5dcba684e888fbc90554cb12a2f4e492927c8c2761a80b7590209821743", size = 758371, upload-time = "2025-08-03T05:05:34.722Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/29/6a7b7f5d47712487d8a3516584a4a484a0147f2537228237397793b2de69/pyzmq-27.0.1-pp39-pypy39_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:95594b2ceeaa94934e3e94dd7bf5f3c3659cf1a26b1fb3edcf6e42dad7e0eaf2", size = 567395, upload-time = "2025-08-03T05:05:36.701Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/37/c1f26d13e9d4c3bfce42fead8ff640f6c06a58decde49a6b295b9d52cefd/pyzmq-27.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:70b719a130b81dd130a57ac0ff636dc2c0127c5b35ca5467d1b67057e3c7a4d2", size = 544561, upload-time = "2025-08-03T05:05:38.608Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/4d/2081cd7e41e340004d2051821efe1d0d67d31bdb5ac33bffc7e628d5f1bd/pyzmq-27.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:8b32c4636ced87dce0ac3d671e578b3400215efab372f1b4be242e8cf0b11384", size = 1329839, upload-time = "2025-08-21T04:20:55.8Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/f1/1300b7e932671e31accb3512c19b43e6a3e8d08c54ab8b920308e53427ce/pyzmq-27.0.2-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f9528a4b3e24189cb333a9850fddbbafaa81df187297cfbddee50447cdb042cf", size = 906367, upload-time = "2025-08-21T04:20:58.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/80/61662db85eb3255a58c1bb59f6d4fc0d31c9c75b9a14983deafab12b2329/pyzmq-27.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b02ba0c0b2b9ebe74688002e6c56c903429924a25630804b9ede1f178aa5a3f", size = 666545, upload-time = "2025-08-21T04:20:59.775Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/6e/49fb9c75b039978cbb1f3657811d8056b0ebe6ecafd78a4457fc6de19799/pyzmq-27.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4dc5c9a6167617251dea0d024d67559795761aabb4b7ea015518be898be076", size = 854219, upload-time = "2025-08-21T04:21:01.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/3c/9951b302d221e471b7c659e70f9cb64db5f68fa3b7da45809ec4e6c6ef17/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f1151b33aaf3b4fa9da26f4d696e38eebab67d1b43c446184d733c700b3ff8ce", size = 1655103, upload-time = "2025-08-21T04:21:03.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/ca/d7adea6100fdf7f87f3856db02d2a0a45ce2764b9f60ba08c48c655b762f/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4ecfc7999ac44c9ef92b5ae8f0b44fb935297977df54d8756b195a3cd12f38f0", size = 2033712, upload-time = "2025-08-21T04:21:05.121Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/63/b34e601b36ba4864d02ac1460443fc39bf533dedbdeead2a4e0df7dfc8ee/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31c26a5d0b00befcaeeb600d8b15ad09f5604b6f44e2057ec5e521a9e18dcd9a", size = 1891847, upload-time = "2025-08-21T04:21:06.586Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/a2/9479e6af779da44f788d5fcda5f77dff1af988351ef91682b92524eab2db/pyzmq-27.0.2-cp310-cp310-win32.whl", hash = "sha256:25a100d2de2ac0c644ecf4ce0b509a720d12e559c77aff7e7e73aa684f0375bc", size = 567136, upload-time = "2025-08-21T04:21:07.885Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/46/e1c2be469781fc56ba092fecb1bb336cedde0fd87d9e1a547aaeb5d1a968/pyzmq-27.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a1acf091f53bb406e9e5e7383e467d1dd1b94488b8415b890917d30111a1fef3", size = 631969, upload-time = "2025-08-21T04:21:09.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/8d/d20a62f1f77e3f04633a80bb83df085e4314f0e9404619cc458d0005d6ab/pyzmq-27.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:b38e01f11e9e95f6668dc8a62dccf9483f454fed78a77447507a0e8dcbd19a63", size = 559459, upload-time = "2025-08-21T04:21:11.208Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/73/034429ab0f4316bf433eb6c20c3f49d1dc13b2ed4e4d951b283d300a0f35/pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d", size = 1333169, upload-time = "2025-08-21T04:21:12.483Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/02/c42b3b526eb03a570c889eea85a5602797f800a50ba8b09ddbf7db568b78/pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076", size = 909176, upload-time = "2025-08-21T04:21:13.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/35/a1c0b988fabbdf2dc5fe94b7c2bcfd61e3533e5109297b8e0daf1d7a8d2d/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1", size = 668972, upload-time = "2025-08-21T04:21:15.315Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/63/908ac865da32ceaeecea72adceadad28ca25b23a2ca5ff018e5bff30116f/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:862aedec0b0684a5050cdb5ec13c2da96d2f8dffda48657ed35e312a4e31553b", size = 856962, upload-time = "2025-08-21T04:21:16.652Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/5a/90b3cc20b65cdf9391896fcfc15d8db21182eab810b7ea05a2986912fbe2/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5bcfc51c7a4fce335d3bc974fd1d6a916abbcdd2b25f6e89d37b8def25f57", size = 1657712, upload-time = "2025-08-21T04:21:18.666Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/3c/32a5a80f9be4759325b8d7b22ce674bb87e586b4c80c6a9d77598b60d6f0/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:38ff75b2a36e3a032e9fef29a5871e3e1301a37464e09ba364e3c3193f62982a", size = 2035054, upload-time = "2025-08-21T04:21:20.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/61/71084fe2ff2d7dc5713f8740d735336e87544845dae1207a8e2e16d9af90/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a5709abe8d23ca158a9d0a18c037f4193f5b6afeb53be37173a41e9fb885792", size = 1894010, upload-time = "2025-08-21T04:21:21.96Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/6b/77169cfb13b696e50112ca496b2ed23c4b7d8860a1ec0ff3e4b9f9926221/pyzmq-27.0.2-cp311-cp311-win32.whl", hash = "sha256:47c5dda2018c35d87be9b83de0890cb92ac0791fd59498847fc4eca6ff56671d", size = 566819, upload-time = "2025-08-21T04:21:23.31Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/cd/86c4083e0f811f48f11bc0ddf1e7d13ef37adfd2fd4f78f2445f1cc5dec0/pyzmq-27.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f54ca3e98f8f4d23e989c7d0edcf9da7a514ff261edaf64d1d8653dd5feb0a8b", size = 633264, upload-time = "2025-08-21T04:21:24.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/69/5b8bb6a19a36a569fac02153a9e083738785892636270f5f68a915956aea/pyzmq-27.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:2ef3067cb5b51b090fb853f423ad7ed63836ec154374282780a62eb866bf5768", size = 559316, upload-time = "2025-08-21T04:21:26.1Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/69/b3a729e7b03e412bee2b1823ab8d22e20a92593634f664afd04c6c9d9ac0/pyzmq-27.0.2-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:5da05e3c22c95e23bfc4afeee6ff7d4be9ff2233ad6cb171a0e8257cd46b169a", size = 1305910, upload-time = "2025-08-21T04:21:27.609Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/b7/f6a6a285193d489b223c340b38ee03a673467cb54914da21c3d7849f1b10/pyzmq-27.0.2-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4520577971d01d47e2559bb3175fce1be9103b18621bf0b241abe0a933d040", size = 895507, upload-time = "2025-08-21T04:21:29.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/e6/c4ed2da5ef9182cde1b1f5d0051a986e76339d71720ec1a00be0b49275ad/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d7de7bf73165b90bd25a8668659ccb134dd28449116bf3c7e9bab5cf8a8ec9", size = 652670, upload-time = "2025-08-21T04:21:30.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/66/d781ab0636570d32c745c4e389b1c6b713115905cca69ab6233508622edd/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340e7cddc32f147c6c00d116a3f284ab07ee63dbd26c52be13b590520434533c", size = 840581, upload-time = "2025-08-21T04:21:32.008Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/df/f24790caf565d72544f5c8d8500960b9562c1dc848d6f22f3c7e122e73d4/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba95693f9df8bb4a9826464fb0fe89033936f35fd4a8ff1edff09a473570afa0", size = 1641931, upload-time = "2025-08-21T04:21:33.371Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/65/77d27b19fc5e845367f9100db90b9fce924f611b14770db480615944c9c9/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:ca42a6ce2d697537da34f77a1960d21476c6a4af3e539eddb2b114c3cf65a78c", size = 2021226, upload-time = "2025-08-21T04:21:35.301Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/65/1ed14421ba27a4207fa694772003a311d1142b7f543179e4d1099b7eb746/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e44e665d78a07214b2772ccbd4b9bcc6d848d7895f1b2d7653f047b6318a4f6", size = 1878047, upload-time = "2025-08-21T04:21:36.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/dc/e578549b89b40dc78a387ec471c2a360766690c0a045cd8d1877d401012d/pyzmq-27.0.2-cp312-abi3-win32.whl", hash = "sha256:272d772d116615397d2be2b1417b3b8c8bc8671f93728c2f2c25002a4530e8f6", size = 558757, upload-time = "2025-08-21T04:21:38.2Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/89/06600980aefcc535c758414da969f37a5194ea4cdb73b745223f6af3acfb/pyzmq-27.0.2-cp312-abi3-win_amd64.whl", hash = "sha256:734be4f44efba0aa69bf5f015ed13eb69ff29bf0d17ea1e21588b095a3147b8e", size = 619281, upload-time = "2025-08-21T04:21:39.909Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/84/df8a5c089552d17c9941d1aea4314b606edf1b1622361dae89aacedc6467/pyzmq-27.0.2-cp312-abi3-win_arm64.whl", hash = "sha256:41f0bd56d9279392810950feb2785a419c2920bbf007fdaaa7f4a07332ae492d", size = 552680, upload-time = "2025-08-21T04:21:41.571Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/7b/b79e976508517ab80dc800f7021ef1fb602a6d55e4caa2d47fb3dca5d8b6/pyzmq-27.0.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:7f01118133427cd7f34ee133b5098e2af5f70303fa7519785c007bca5aa6f96a", size = 1122259, upload-time = "2025-08-21T04:21:43.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/1c/777217b9940ebcb7e71c924184ca5f31e410580a58d9fd93798589f0d31c/pyzmq-27.0.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:e4b860edf6379a7234ccbb19b4ed2c57e3ff569c3414fadfb49ae72b61a8ef07", size = 1156113, upload-time = "2025-08-21T04:21:44.566Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/7d/654657a4c6435f41538182e71b61eac386a789a2bbb6f30171915253a9a7/pyzmq-27.0.2-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:cb77923ea163156da14295c941930bd525df0d29c96c1ec2fe3c3806b1e17cb3", size = 1341437, upload-time = "2025-08-21T04:21:46.019Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/a0/5ed7710037f9c096017adc748bcb1698674a2d297f8b9422d38816f7b56a/pyzmq-27.0.2-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:61678b7407b04df8f9423f188156355dc94d0fb52d360ae79d02ed7e0d431eea", size = 897888, upload-time = "2025-08-21T04:21:47.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/8a/6e4699a60931c17e7406641d201d7f2c121e2a38979bc83226a6d8f1ba32/pyzmq-27.0.2-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3c824b70925963bdc8e39a642672c15ffaa67e7d4b491f64662dd56d6271263", size = 660727, upload-time = "2025-08-21T04:21:48.734Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/d8/d761e438c186451bd89ce63a665cde5690c084b61cd8f5d7b51e966e875a/pyzmq-27.0.2-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4833e02fcf2751975457be1dfa2f744d4d09901a8cc106acaa519d868232175", size = 848136, upload-time = "2025-08-21T04:21:50.416Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/f1/a0f31684efdf3eb92f46b7dd2117e752208115e89d278f8ca5f413c5bb85/pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b18045668d09cf0faa44918af2a67f0dbbef738c96f61c2f1b975b1ddb92ccfc", size = 1650402, upload-time = "2025-08-21T04:21:52.235Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/fd/0d7f2a1732812df02c85002770da4a7864c79b210084bcdab01ea57e8d92/pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bbbb7e2f3ac5a22901324e7b086f398b8e16d343879a77b15ca3312e8cd8e6d5", size = 2024587, upload-time = "2025-08-21T04:21:54.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/73/358be69e279a382dd09e46dda29df8446365cddee4f79ef214e71e5b2b5a/pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b751914a73604d40d88a061bab042a11d4511b3ddbb7624cd83c39c8a498564c", size = 1885493, upload-time = "2025-08-21T04:21:55.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/7b/e9951ad53b3dfed8cfb4c2cfd6e0097c9b454e5c0d0e6df5f2b60d7c8c3d/pyzmq-27.0.2-cp313-cp313t-win32.whl", hash = "sha256:3e8f833dd82af11db5321c414638045c70f61009f72dd61c88db4a713c1fb1d2", size = 574934, upload-time = "2025-08-21T04:21:57.52Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/33/1a7fc3a92f2124a63e6e2a6afa0af471a5c0c713e776b476d4eda5111b13/pyzmq-27.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5b45153cb8eadcab14139970643a84f7a7b08dda541fbc1f6f4855c49334b549", size = 640932, upload-time = "2025-08-21T04:21:59.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/52/2598a94ac251a7c83f3887866225eea1952b0d4463a68df5032eb00ff052/pyzmq-27.0.2-cp313-cp313t-win_arm64.whl", hash = "sha256:86898f5c9730df23427c1ee0097d8aa41aa5f89539a79e48cd0d2c22d059f1b7", size = 561315, upload-time = "2025-08-21T04:22:01.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/7d/10ef02ea36590b29d48ef88eb0831f0af3eb240cccca2752556faec55f59/pyzmq-27.0.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d2b4b261dce10762be5c116b6ad1f267a9429765b493c454f049f33791dd8b8a", size = 1341463, upload-time = "2025-08-21T04:22:02.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/36/115d18dade9a3d4d3d08dd8bfe5459561b8e02815f99df040555fdd7768e/pyzmq-27.0.2-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4d88b6cff156fed468903006b24bbd85322612f9c2f7b96e72d5016fd3f543", size = 897840, upload-time = "2025-08-21T04:22:04.845Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/66/083b37839b95c386a95f1537bb41bdbf0c002b7c55b75ee737949cecb11f/pyzmq-27.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8426c0ebbc11ed8416a6e9409c194142d677c2c5c688595f2743664e356d9e9b", size = 660704, upload-time = "2025-08-21T04:22:06.389Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/5a/196ab46e549ba35bf3268f575e10cfac0dc86b78dcaa7a3e36407ecda752/pyzmq-27.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565bee96a155fe6452caed5fb5f60c9862038e6b51a59f4f632562081cdb4004", size = 848037, upload-time = "2025-08-21T04:22:07.817Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/ea/a27b9eb44b2e615a9ecb8510ebb023cc1d2d251181e4a1e50366bfbf94d6/pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5de735c745ca5cefe9c2d1547d8f28cfe1b1926aecb7483ab1102fd0a746c093", size = 1650278, upload-time = "2025-08-21T04:22:09.269Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/ac/3e9af036bfaf718ab5e69ded8f6332da392c5450ad43e8e3ca66797f145a/pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ea4f498f8115fd90d7bf03a3e83ae3e9898e43362f8e8e8faec93597206e15cc", size = 2024504, upload-time = "2025-08-21T04:22:10.778Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/e9/3202d31788df8ebaa176b23d846335eb9c768d8b43c0506bbd6265ad36a0/pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d00e81cb0afd672915257a3927124ee2ad117ace3c256d39cd97ca3f190152ad", size = 1885381, upload-time = "2025-08-21T04:22:12.718Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/ed/42de80b7ab4e8fcf13376f81206cf8041740672ac1fd2e1c598d63f595bf/pyzmq-27.0.2-cp314-cp314t-win32.whl", hash = "sha256:0f6e9b00d81b58f859fffc112365d50413954e02aefe36c5b4c8fb4af79f8cc3", size = 587526, upload-time = "2025-08-21T04:22:14.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/c8/8f3c72d6f0bfbf090aa5e283576073ca5c59839b85a5cc8c66ddb9b59801/pyzmq-27.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:2e73cf3b127a437fef4100eb3ac2ebe6b49e655bb721329f667f59eca0a26221", size = 661368, upload-time = "2025-08-21T04:22:15.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/a4/7ee652ea1c77d872f5d99ed937fa8bbd1f6f4b7a39a6d3a0076c286e0c3e/pyzmq-27.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4108785f2e5ac865d06f678a07a1901e3465611356df21a545eeea8b45f56265", size = 574901, upload-time = "2025-08-21T04:22:17.423Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/57/a4aff0dbb29001cebf94848a980ac36bea8e1cb65ac0f72ff03e484df208/pyzmq-27.0.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:aa9c1c208c263b84386ac25bed6af5672397dc3c232638114fc09bca5c7addf9", size = 1330291, upload-time = "2025-08-21T04:22:34.433Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/a6/8519ed48b5bdd72e1a3946a76987afb6659a355180cd15e51cea6afe1983/pyzmq-27.0.2-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:795c4884cfe7ea59f2b67d82b417e899afab889d332bfda13b02f8e0c155b2e4", size = 906596, upload-time = "2025-08-21T04:22:36.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/19/cb6f463d6454079a566160d10cf175c3c59274b1378104d2b01a5a1ca560/pyzmq-27.0.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47eb65bb25478358ba3113dd9a08344f616f417ad3ffcbb190cd874fae72b1b1", size = 863605, upload-time = "2025-08-21T04:22:37.825Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/37/afe25d5f9b91b7422067bb7cd3f75691d78baa0c2c2f76bd0643389ac600/pyzmq-27.0.2-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6fc24f00293f10aff04d55ca37029b280474c91f4de2cad5e911e5e10d733b7", size = 666777, upload-time = "2025-08-21T04:22:39.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/4a/97b0b59a7eacfd9e61c40abaa9c7e2587bbb39671816a9fb0c5dc32d87bc/pyzmq-27.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58d4cc9b6b768478adfc40a5cbee545303db8dbc81ba688474e0f499cc581028", size = 1655404, upload-time = "2025-08-21T04:22:41.082Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/db/bcf03bea232839ea749562f76bda3807b16a25c68fdd153339a56699bb1d/pyzmq-27.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea2f26c5972796e02b222968a21a378d09eb4ff590eb3c5fafa8913f8c2bdf5", size = 2034051, upload-time = "2025-08-21T04:22:43.216Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/3f/9cc291101ee766ab41bae25654af84c11d259f27d73b1ed985256dae8a16/pyzmq-27.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a0621ec020c49fc1b6e31304f1a820900d54e7d9afa03ea1634264bf9387519e", size = 1892080, upload-time = "2025-08-21T04:22:44.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/52/51d4c56c553e7368abc2d010a783092c68a1888045f899eabcfd3bc345f3/pyzmq-27.0.2-cp39-cp39-win32.whl", hash = "sha256:1326500792a9cb0992db06bbaf5d0098459133868932b81a6e90d45c39eca99d", size = 567463, upload-time = "2025-08-21T04:22:46.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/20/3b0ac853b5bbe1fd05e1898ea60335f9d402312328fa17514a876418ab11/pyzmq-27.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:5ee9560cb1e3094ef01fc071b361121a57ebb8d4232912b6607a6d7d2d0a97b4", size = 632200, upload-time = "2025-08-21T04:22:48.34Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/14/78b192a7864a38c71e30c3fd668eff949b44db13c1906035e6095fecd00d/pyzmq-27.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:85e3c6fb0d25ea046ebcfdc2bcb9683d663dc0280645c79a616ff5077962a15b", size = 559638, upload-time = "2025-08-21T04:22:50.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/d7/e388e80107b7c438c9698ce59c2a3b950021cd4ab3fe641485e4ed6b0960/pyzmq-27.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d67a0960803a37b60f51b460c58444bc7033a804c662f5735172e21e74ee4902", size = 836008, upload-time = "2025-08-21T04:22:51.842Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/ef/58d3eb85f1b67a16e22adb07d084f975a7b9641463d18e27230550bb436a/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:dd4d3e6a567ffd0d232cfc667c49d0852d0ee7481458a2a1593b9b1bc5acba88", size = 799932, upload-time = "2025-08-21T04:22:53.529Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/63/66b9f6db19ee8c86105ffd4475a4f5d93cdd62b1edcb1e894d971df0728c/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e558be423631704803bc6a642e2caa96083df759e25fe6eb01f2d28725f80bd", size = 567458, upload-time = "2025-08-21T04:22:55.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/af/d92207fe8b6e3d9f588d0591219a86dd7b4ed27bb3e825c1d9cf48467fc0/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4c20ba8389f495c7b4f6b896bb1ca1e109a157d4f189267a902079699aaf787", size = 747087, upload-time = "2025-08-21T04:22:56.994Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/e9/d9f8b4b191c6733e31de28974d608a2475a6598136ac901a8c5b67c11432/pyzmq-27.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c5be232f7219414ff672ff7ab8c5a7e8632177735186d8a42b57b491fafdd64e", size = 544641, upload-time = "2025-08-21T04:22:58.87Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/60/027d0032a1e3b1aabcef0e309b9ff8a4099bdd5a60ab38b36a676ff2bd7b/pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02", size = 836007, upload-time = "2025-08-21T04:23:00.447Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/20/2ed1e6168aaea323df9bb2c451309291f53ba3af372ffc16edd4ce15b9e5/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f", size = 799932, upload-time = "2025-08-21T04:23:02.052Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/25/5c147307de546b502c9373688ce5b25dc22288d23a1ebebe5d587bf77610/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b", size = 567459, upload-time = "2025-08-21T04:23:03.593Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/06/0dc56ffc615c8095cd089c9b98ce5c733e990f09ce4e8eea4aaf1041a532/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41", size = 747088, upload-time = "2025-08-21T04:23:05.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/f6/4a50187e023b8848edd3f0a8e197b1a7fb08d261d8c60aae7cb6c3d71612/pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb", size = 544639, upload-time = "2025-08-21T04:23:07.279Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/ce/36c82244b111c408c99037b61eedcaac9a3f17300c3f857cf175a7b8832f/pyzmq-27.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:400f34321e3bd89b1165b91ea6b18ad26042ba9ad0dfed8b35049e2e24eeab9b", size = 836001, upload-time = "2025-08-21T04:23:17.532Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/dc/4aa8e518f1c0d0ee7fb98cf54cf3854e471815040062d26d948df638d9bc/pyzmq-27.0.2-pp39-pypy39_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9cbad4ef12e4c15c94d2c24ecd15a8ed56bf091c62f121a2b0c618ddd4b7402b", size = 799927, upload-time = "2025-08-21T04:23:19.218Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/4a/cca781133ce015898b7c4ad63240d05af5c54e8736d099c95b0a6c9aaca9/pyzmq-27.0.2-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6b2b74aac3392b8cf508ccb68c980a8555298cd378434a2d065d6ce0f4211dff", size = 758427, upload-time = "2025-08-21T04:23:21.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/66/772bf1e3aed8b20587f2e0122bc929711d44ddd27dbbd6eb308a5badd0c3/pyzmq-27.0.2-pp39-pypy39_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7db5db88c24cf9253065d69229a148ff60821e5d6f8ff72579b1f80f8f348bab", size = 567453, upload-time = "2025-08-21T04:23:22.825Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/fb/fdc3dd8ec46f5226b4cde299839f10c625886bd18adbeaa8a59ffe104356/pyzmq-27.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8ffe40c216c41756ca05188c3e24a23142334b304f7aebd75c24210385e35573", size = 544636, upload-time = "2025-08-21T04:23:24.736Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2995,28 +2995,28 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.12.9"
|
||||
version = "0.12.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4a/45/2e403fa7007816b5fbb324cb4f8ed3c7402a927a0a0cb2b6279879a8bfdc/ruff-0.12.9.tar.gz", hash = "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", size = 5254702, upload-time = "2025-08-14T16:08:55.2Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3b/eb/8c073deb376e46ae767f4961390d17545e8535921d2f65101720ed8bd434/ruff-0.12.10.tar.gz", hash = "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", size = 5310076, upload-time = "2025-08-21T18:23:22.595Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/20/53bf098537adb7b6a97d98fcdebf6e916fcd11b2e21d15f8c171507909cc/ruff-0.12.9-py3-none-linux_armv6l.whl", hash = "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e", size = 11759705, upload-time = "2025-08-14T16:08:12.968Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/4d/c764ee423002aac1ec66b9d541285dd29d2c0640a8086c87de59ebbe80d5/ruff-0.12.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", size = 12527042, upload-time = "2025-08-14T16:08:16.54Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/45/cfcdf6d3eb5fc78a5b419e7e616d6ccba0013dc5b180522920af2897e1be/ruff-0.12.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", size = 11724457, upload-time = "2025-08-14T16:08:18.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/e6/44615c754b55662200c48bebb02196dbb14111b6e266ab071b7e7297b4ec/ruff-0.12.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", size = 11949446, upload-time = "2025-08-14T16:08:21.059Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/d1/9b7d46625d617c7df520d40d5ac6cdcdf20cbccb88fad4b5ecd476a6bb8d/ruff-0.12.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", size = 11566350, upload-time = "2025-08-14T16:08:23.433Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/20/b73132f66f2856bc29d2d263c6ca457f8476b0bbbe064dac3ac3337a270f/ruff-0.12.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", size = 13270430, upload-time = "2025-08-14T16:08:25.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/21/eaf3806f0a3d4c6be0a69d435646fba775b65f3f2097d54898b0fd4bb12e/ruff-0.12.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", size = 14264717, upload-time = "2025-08-14T16:08:27.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/82/1d0c53bd37dcb582b2c521d352fbf4876b1e28bc0d8894344198f6c9950d/ruff-0.12.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", size = 13684331, upload-time = "2025-08-14T16:08:30.352Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/2f/1c5cf6d8f656306d42a686f1e207f71d7cebdcbe7b2aa18e4e8a0cb74da3/ruff-0.12.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", size = 12739151, upload-time = "2025-08-14T16:08:32.55Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/09/25033198bff89b24d734e6479e39b1968e4c992e82262d61cdccaf11afb9/ruff-0.12.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", size = 12954992, upload-time = "2025-08-14T16:08:34.816Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/8e/d0dbf2f9dca66c2d7131feefc386523404014968cd6d22f057763935ab32/ruff-0.12.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", size = 12899569, upload-time = "2025-08-14T16:08:36.852Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/bd/b614d7c08515b1428ed4d3f1d4e3d687deffb2479703b90237682586fa66/ruff-0.12.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", size = 11751983, upload-time = "2025-08-14T16:08:39.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/d6/383e9f818a2441b1a0ed898d7875f11273f10882f997388b2b51cb2ae8b5/ruff-0.12.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", size = 11538635, upload-time = "2025-08-14T16:08:41.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/9c/56f869d314edaa9fc1f491706d1d8a47747b9d714130368fbd69ce9024e9/ruff-0.12.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", size = 12534346, upload-time = "2025-08-14T16:08:43.39Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/4b/d8b95c6795a6c93b439bc913ee7a94fda42bb30a79285d47b80074003ee7/ruff-0.12.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", size = 13017021, upload-time = "2025-08-14T16:08:45.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/c1/5f9a839a697ce1acd7af44836f7c2181cdae5accd17a5cb85fcbd694075e/ruff-0.12.9-py3-none-win32.whl", hash = "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", size = 11734785, upload-time = "2025-08-14T16:08:48.062Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/66/cdddc2d1d9a9f677520b7cfc490d234336f523d4b429c1298de359a3be08/ruff-0.12.9-py3-none-win_amd64.whl", hash = "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", size = 12840654, upload-time = "2025-08-14T16:08:50.158Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/e7/560d049d15585d6c201f9eeacd2fd130def3741323e5ccf123786e0e3c95/ruff-0.12.10-py3-none-linux_armv6l.whl", hash = "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", size = 11935161, upload-time = "2025-08-21T18:22:26.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/b0/ad2464922a1113c365d12b8f80ed70fcfb39764288ac77c995156080488d/ruff-0.12.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", size = 12660884, upload-time = "2025-08-21T18:22:30.925Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/f1/97f509b4108d7bae16c48389f54f005b62ce86712120fd8b2d8e88a7cb49/ruff-0.12.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", size = 11872754, upload-time = "2025-08-21T18:22:34.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/ad/44f606d243f744a75adc432275217296095101f83f966842063d78eee2d3/ruff-0.12.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", size = 12092276, upload-time = "2025-08-21T18:22:36.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/1f/ed6c265e199568010197909b25c896d66e4ef2c5e1c3808caf461f6f3579/ruff-0.12.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", size = 11734700, upload-time = "2025-08-21T18:22:39.822Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/c5/b21cde720f54a1d1db71538c0bc9b73dee4b563a7dd7d2e404914904d7f5/ruff-0.12.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", size = 13468783, upload-time = "2025-08-21T18:22:42.559Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9e/39369e6ac7f2a1848f22fb0b00b690492f20811a1ac5c1fd1d2798329263/ruff-0.12.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", size = 14436642, upload-time = "2025-08-21T18:22:45.612Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/03/5da8cad4b0d5242a936eb203b58318016db44f5c5d351b07e3f5e211bb89/ruff-0.12.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", size = 13859107, upload-time = "2025-08-21T18:22:48.886Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/19/dd7273b69bf7f93a070c9cec9494a94048325ad18fdcf50114f07e6bf417/ruff-0.12.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", size = 12886521, upload-time = "2025-08-21T18:22:51.567Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/1d/b4207ec35e7babaee62c462769e77457e26eb853fbdc877af29417033333/ruff-0.12.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", size = 13097528, upload-time = "2025-08-21T18:22:54.609Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/00/58f7b873b21114456e880b75176af3490d7a2836033779ca42f50de3b47a/ruff-0.12.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", size = 13080443, upload-time = "2025-08-21T18:22:57.413Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/8c/9e6660007fb10189ccb78a02b41691288038e51e4788bf49b0a60f740604/ruff-0.12.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60", size = 11896759, upload-time = "2025-08-21T18:23:00.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/4c/6d092bb99ea9ea6ebda817a0e7ad886f42a58b4501a7e27cd97371d0ba54/ruff-0.12.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", size = 11701463, upload-time = "2025-08-21T18:23:03.211Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/80/d982c55e91df981f3ab62559371380616c57ffd0172d96850280c2b04fa8/ruff-0.12.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", size = 12691603, upload-time = "2025-08-21T18:23:06.935Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/37/63a9c788bbe0b0850611669ec6b8589838faf2f4f959647f2d3e320383ae/ruff-0.12.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", size = 13164356, upload-time = "2025-08-21T18:23:10.225Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/d4/1aaa7fb201a74181989970ebccd12f88c0fc074777027e2a21de5a90657e/ruff-0.12.10-py3-none-win32.whl", hash = "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", size = 11896089, upload-time = "2025-08-21T18:23:14.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/14/2ad38fd4037daab9e023456a4a40ed0154e9971f8d6aed41bdea390aabd9/ruff-0.12.10-py3-none-win_amd64.whl", hash = "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", size = 13004616, upload-time = "2025-08-21T18:23:17.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3106,15 +3106,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.47.2"
|
||||
version = "0.47.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144, upload-time = "2025-08-24T13:36:42.122Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991, upload-time = "2025-08-24T13:36:40.887Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3251,11 +3251,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "types-python-dateutil"
|
||||
version = "2.9.0.20250809"
|
||||
version = "2.9.0.20250822"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a3/53/07dac71db45fb6b3c71c2fd29a87cada2239eac7ecfb318e6ebc7da00a3b/types_python_dateutil-2.9.0.20250809.tar.gz", hash = "sha256:69cbf8d15ef7a75c3801d65d63466e46ac25a0baa678d89d0a137fc31a608cc1", size = 15820, upload-time = "2025-08-09T03:14:14.109Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0c/0a/775f8551665992204c756be326f3575abba58c4a3a52eef9909ef4536428/types_python_dateutil-2.9.0.20250822.tar.gz", hash = "sha256:84c92c34bd8e68b117bff742bc00b692a1e8531262d4507b33afcc9f7716cd53", size = 16084, upload-time = "2025-08-22T03:02:00.613Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/43/5e/67312e679f612218d07fcdbd14017e6d571ce240a5ba1ad734f15a8523cc/types_python_dateutil-2.9.0.20250809-py3-none-any.whl", hash = "sha256:768890cac4f2d7fd9e0feb6f3217fce2abbfdfc0cadd38d11fba325a815e4b9f", size = 17707, upload-time = "2025-08-09T03:14:13.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/d9/a29dfa84363e88b053bf85a8b7f212a04f0d7343a4d24933baa45c06e08b/types_python_dateutil-2.9.0.20250822-py3-none-any.whl", hash = "sha256:849d52b737e10a6dc6621d2bd7940ec7c65fcb69e6aa2882acf4e56b2b508ddc", size = 17892, upload-time = "2025-08-22T03:01:59.436Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3272,11 +3272,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.14.1"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -38,7 +38,6 @@ from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
|
||||
from langgraph._internal._runnable import RunnableCallable, RunnableLike
|
||||
from langgraph._internal._typing import MISSING
|
||||
from langgraph.agent.types import AgentMiddleware
|
||||
from langgraph.errors import ErrorCode, create_error_message
|
||||
from langgraph.graph import END, StateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
@@ -262,7 +261,6 @@ def create_react_agent(
|
||||
],
|
||||
tools: Union[Sequence[Union[BaseTool, Callable, dict[str, Any]]], ToolNode],
|
||||
*,
|
||||
middleware: Sequence[AgentMiddleware] = (),
|
||||
prompt: Optional[Prompt] = None,
|
||||
response_format: Optional[
|
||||
Union[StructuredResponseSchema, tuple[str, StructuredResponseSchema]]
|
||||
@@ -392,6 +390,14 @@ def create_react_agent(
|
||||
state_schema: An optional state schema that defines graph state.
|
||||
Must have `messages` and `remaining_steps` keys.
|
||||
Defaults to `AgentState` that defines those two keys.
|
||||
!!! Note
|
||||
`remaining_steps` is used to limit the number of steps the react agent can take.
|
||||
Calculated roughly as `recursion_limit` - `total_steps_taken`.
|
||||
If `remaining_steps` is less than 2 and tool calls are present in the response,
|
||||
the react agent will return a final AI Message with
|
||||
the content "Sorry, need more steps to process this request.".
|
||||
No `GraphRecusionError` will be raised in this case.
|
||||
|
||||
context_schema: An optional schema for runtime context.
|
||||
checkpointer: An optional checkpoint saver object. This is used for persisting
|
||||
the state of the graph (e.g., as chat memory) for a single thread (e.g., a single conversation).
|
||||
@@ -464,32 +470,6 @@ def create_react_agent(
|
||||
print(chunk)
|
||||
```
|
||||
"""
|
||||
if middleware:
|
||||
assert isinstance(model, str | BaseChatModel)
|
||||
assert isinstance(prompt, str | None)
|
||||
assert not isinstance(response_format, tuple)
|
||||
assert pre_model_hook is None
|
||||
assert post_model_hook is None
|
||||
assert state_schema is None
|
||||
|
||||
from langgraph.agent import create_agent
|
||||
|
||||
return create_agent(
|
||||
model=model,
|
||||
tools=tools,
|
||||
system_prompt=prompt,
|
||||
middleware=middleware,
|
||||
response_format=response_format,
|
||||
context_schema=context_schema,
|
||||
).compile(
|
||||
checkpointer=checkpointer,
|
||||
store=store,
|
||||
name=name,
|
||||
interrupt_after=interrupt_after,
|
||||
interrupt_before=interrupt_before,
|
||||
debug=debug,
|
||||
)
|
||||
|
||||
if (
|
||||
config_schema := deprecated_kwargs.pop("config_schema", MISSING)
|
||||
) is not MISSING:
|
||||
|
||||
Generated
+1
-1
@@ -316,7 +316,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
source = { editable = "../langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from langgraph_sdk.auth import Auth
|
||||
from langgraph_sdk.client import get_client, get_sync_client
|
||||
|
||||
__version__ = "0.2.3"
|
||||
__version__ = "0.2.6"
|
||||
|
||||
__all__ = ["Auth", "get_client", "get_sync_client"]
|
||||
|
||||
@@ -400,6 +400,20 @@ class AuthContext(BaseAuthContext):
|
||||
"""
|
||||
|
||||
|
||||
class ThreadTTL(typing.TypedDict, total=False):
|
||||
"""Time-to-live configuration for a thread.
|
||||
|
||||
Matches the OpenAPI schema where TTL is represented as an object with
|
||||
an optional strategy and a time value in minutes.
|
||||
"""
|
||||
|
||||
strategy: typing.Literal["delete"]
|
||||
"""TTL strategy. Currently only 'delete' is supported."""
|
||||
|
||||
ttl: int
|
||||
"""Time-to-live in minutes from now until the thread should be swept."""
|
||||
|
||||
|
||||
class ThreadsCreate(typing.TypedDict, total=False):
|
||||
"""Parameters for creating a new thread.
|
||||
|
||||
@@ -422,6 +436,9 @@ class ThreadsCreate(typing.TypedDict, total=False):
|
||||
if_exists: OnConflictBehavior
|
||||
"""Behavior when a thread with the same ID already exists."""
|
||||
|
||||
ttl: ThreadTTL
|
||||
"""Optional TTL configuration for the thread."""
|
||||
|
||||
|
||||
class ThreadsRead(typing.TypedDict, total=False):
|
||||
"""Parameters for reading thread state or run information.
|
||||
@@ -489,6 +506,9 @@ class ThreadsSearch(typing.TypedDict, total=False):
|
||||
offset: int
|
||||
"""Offset for pagination."""
|
||||
|
||||
ids: Sequence[UUID] | None
|
||||
"""typing.Optional list of thread IDs to filter by."""
|
||||
|
||||
thread_id: UUID | None
|
||||
"""typing.Optional thread ID to filter by."""
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
|
||||
from types import TracebackType
|
||||
from typing import (
|
||||
@@ -45,6 +46,7 @@ from langgraph_sdk.schema import (
|
||||
CronSelectField,
|
||||
CronSortBy,
|
||||
DisconnectMode,
|
||||
Durability,
|
||||
GraphSchema,
|
||||
IfNotExists,
|
||||
Item,
|
||||
@@ -69,6 +71,7 @@ from langgraph_sdk.schema import (
|
||||
ThreadSortBy,
|
||||
ThreadState,
|
||||
ThreadStatus,
|
||||
ThreadStreamMode,
|
||||
ThreadUpdateStateResponse,
|
||||
)
|
||||
from langgraph_sdk.sse import SSEDecoder, aiter_lines_raw, iter_lines_raw
|
||||
@@ -154,27 +157,38 @@ def get_client(
|
||||
headers: Mapping[str, str] | None = None,
|
||||
timeout: TimeoutTypes | None = None,
|
||||
) -> LangGraphClient:
|
||||
"""Get a LangGraphClient instance.
|
||||
"""Create and configure a LangGraphClient.
|
||||
|
||||
The client provides programmatic access to a LangGraph Platform deployment. It supports
|
||||
both remote servers and local in-process connections (when running inside a LangGraph server).
|
||||
|
||||
Args:
|
||||
url: The URL of the LangGraph API.
|
||||
api_key: The API key. If not provided, it will be read from the environment.
|
||||
Precedence:
|
||||
1. explicit argument
|
||||
2. LANGGRAPH_API_KEY
|
||||
3. LANGSMITH_API_KEY
|
||||
4. LANGCHAIN_API_KEY
|
||||
headers: Optional custom headers
|
||||
timeout: Optional timeout configuration for the HTTP client.
|
||||
Accepts an httpx.Timeout instance, a float (seconds), or a tuple of timeouts.
|
||||
Tuple format is (connect, read, write, pool)
|
||||
If not provided, defaults to connect=5s, read=300s, write=300s, and pool=5s.
|
||||
url:
|
||||
Base URL of the LangGraph API.
|
||||
– If `None`, the client first attempts an in-process connection via ASGI transport.
|
||||
If that fails, it falls back to `http://localhost:8123`.
|
||||
api_key:
|
||||
API key for authentication. If omitted, the client reads from environment
|
||||
variables in the following order:
|
||||
1. Function argument
|
||||
2. `LANGGRAPH_API_KEY`
|
||||
3. `LANGSMITH_API_KEY`
|
||||
4. `LANGCHAIN_API_KEY`
|
||||
headers:
|
||||
Additional HTTP headers to include in requests. Merged with authentication headers.
|
||||
timeout:
|
||||
HTTP timeout configuration. May be:
|
||||
– `httpx.Timeout` instance
|
||||
– float (total seconds)
|
||||
– tuple `(connect, read, write, pool)` in seconds
|
||||
Defaults: connect=5, read=300, write=300, pool=5.
|
||||
|
||||
Returns:
|
||||
LangGraphClient: The top-level client for accessing AssistantsClient,
|
||||
ThreadsClient, RunsClient, and CronClient.
|
||||
LangGraphClient:
|
||||
A top-level client exposing sub-clients for assistants, threads,
|
||||
runs, and cron operations.
|
||||
|
||||
???+ example "Example"
|
||||
???+ example "Connect to a remote server:"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
@@ -185,6 +199,21 @@ def get_client(
|
||||
# example usage: client.<model>.<method_name>()
|
||||
assistants = await client.assistants.get(assistant_id="some_uuid")
|
||||
```
|
||||
|
||||
???+ example "Connect in-process to a running LangGraph server:"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=None)
|
||||
|
||||
async def my_node(...):
|
||||
subagent_result = await client.runs.wait(
|
||||
thread_id=None,
|
||||
assistant_id="agent",
|
||||
input={"messages": [{"role": "user", "content": "Foo"}]},
|
||||
)
|
||||
```
|
||||
"""
|
||||
|
||||
transport: httpx.AsyncBaseTransport | None = None
|
||||
@@ -1176,6 +1205,7 @@ class ThreadsClient:
|
||||
if_exists: OnConflictBehavior | None = None,
|
||||
supersteps: Sequence[dict[str, Sequence[dict[str, Any]]]] | None = None,
|
||||
graph_id: str | None = None,
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -1190,6 +1220,9 @@ class ThreadsClient:
|
||||
supersteps: Apply a list of supersteps when creating a thread, each containing a sequence of updates.
|
||||
Each update has `values` or `command` and `as_node`. Used for copying a thread between deployments.
|
||||
graph_id: Optional graph ID to associate with the thread.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
@@ -1231,6 +1264,11 @@ class ThreadsClient:
|
||||
}
|
||||
for s in supersteps
|
||||
]
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
|
||||
return await self.http.post(
|
||||
"/threads", json=payload, headers=headers, params=params
|
||||
@@ -1241,6 +1279,7 @@ class ThreadsClient:
|
||||
thread_id: str,
|
||||
*,
|
||||
metadata: Mapping[str, Any],
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -1249,6 +1288,9 @@ class ThreadsClient:
|
||||
Args:
|
||||
thread_id: ID of thread to update.
|
||||
metadata: Metadata to merge with existing thread metadata.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
@@ -1262,12 +1304,19 @@ class ThreadsClient:
|
||||
thread = await client.threads.update(
|
||||
thread_id="my-thread-id",
|
||||
metadata={"number":1},
|
||||
ttl=43_200,
|
||||
)
|
||||
```
|
||||
""" # noqa: E501
|
||||
payload: dict[str, Any] = {"metadata": metadata}
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
return await self.http.patch(
|
||||
f"/threads/{thread_id}",
|
||||
json={"metadata": metadata},
|
||||
json=payload,
|
||||
headers=headers,
|
||||
params=params,
|
||||
)
|
||||
@@ -1306,6 +1355,7 @@ class ThreadsClient:
|
||||
*,
|
||||
metadata: Json = None,
|
||||
values: Json = None,
|
||||
ids: Sequence[str] | None = None,
|
||||
status: ThreadStatus | None = None,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
@@ -1320,6 +1370,7 @@ class ThreadsClient:
|
||||
Args:
|
||||
metadata: Thread metadata to filter on.
|
||||
values: State values to filter on.
|
||||
ids: List of thread IDs to filter by.
|
||||
status: Thread status to filter on.
|
||||
Must be one of 'idle', 'busy', 'interrupted' or 'error'.
|
||||
limit: Limit on number of threads to return.
|
||||
@@ -1353,6 +1404,8 @@ class ThreadsClient:
|
||||
payload["metadata"] = metadata
|
||||
if values:
|
||||
payload["values"] = values
|
||||
if ids:
|
||||
payload["ids"] = ids
|
||||
if status:
|
||||
payload["status"] = status
|
||||
if sort_by:
|
||||
@@ -1682,6 +1735,53 @@ class ThreadsClient:
|
||||
params=params,
|
||||
)
|
||||
|
||||
async def join_stream(
|
||||
self,
|
||||
thread_id: str,
|
||||
*,
|
||||
last_event_id: str | None = None,
|
||||
stream_mode: ThreadStreamMode | Sequence[ThreadStreamMode] = "run_modes",
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> AsyncIterator[StreamPart]:
|
||||
"""Get a stream of events for a thread.
|
||||
|
||||
Args:
|
||||
thread_id: The ID of the thread to get the stream for.
|
||||
last_event_id: The ID of the last event to get.
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
Returns:
|
||||
Iterator[StreamPart]: An iterator of stream parts.
|
||||
|
||||
???+ example "Example Usage"
|
||||
|
||||
```python
|
||||
|
||||
for chunk in client.threads.join_stream(
|
||||
thread_id="my_thread_id",
|
||||
last_event_id="my_event_id",
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
""" # noqa: E501
|
||||
query_params = {
|
||||
"stream_mode": stream_mode,
|
||||
}
|
||||
if params:
|
||||
query_params.update(params)
|
||||
return self.http.stream(
|
||||
f"/threads/{thread_id}/stream",
|
||||
"GET",
|
||||
headers={
|
||||
**({"Last-Event-ID": last_event_id} if last_event_id else {}),
|
||||
**(headers or {}),
|
||||
},
|
||||
params=query_params,
|
||||
)
|
||||
|
||||
|
||||
class RunsClient:
|
||||
"""Client for managing runs in LangGraph.
|
||||
@@ -1772,7 +1872,7 @@ class RunsClient:
|
||||
context: Context | None = None,
|
||||
checkpoint: Checkpoint | None = None,
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
checkpoint_during: bool | None = None, # deprecated
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
feedback_keys: Sequence[str] | None = None,
|
||||
@@ -1785,6 +1885,7 @@ class RunsClient:
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
|
||||
durability: Durability | None = None,
|
||||
) -> AsyncIterator[StreamPart]:
|
||||
"""Create a run and stream the results.
|
||||
|
||||
@@ -1804,7 +1905,7 @@ class RunsClient:
|
||||
context: Static context to add to the assistant.
|
||||
!!! version-added "Supported with langgraph>=0.6.0"
|
||||
checkpoint: The checkpoint to resume from.
|
||||
checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption).
|
||||
checkpoint_during: (deprecated) Whether to checkpoint during the run (or only at the end/interruption).
|
||||
interrupt_before: Nodes to interrupt immediately before they get executed.
|
||||
interrupt_after: Nodes to Nodes to interrupt immediately after they get executed.
|
||||
feedback_keys: Feedback keys to assign to run.
|
||||
@@ -1822,6 +1923,10 @@ class RunsClient:
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
on_run_created: Callback when a run is created.
|
||||
durability: The durability to use for the run. Values are "sync", "async", or "exit".
|
||||
"async" means checkpoints are persisted async while next graph step executes, replaces checkpoint_during=True
|
||||
"sync" means checkpoints are persisted sync after graph step executes, replaces checkpoint_during=False
|
||||
"exit" means checkpoints are only persisted when the run exits, does not save intermediate steps
|
||||
|
||||
Returns:
|
||||
AsyncIterator[StreamPart]: Asynchronous iterator of stream results.
|
||||
@@ -1857,6 +1962,13 @@ class RunsClient:
|
||||
```
|
||||
|
||||
""" # noqa: E501
|
||||
if checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed in a future version. Use `durability` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
payload = {
|
||||
"input": input,
|
||||
"command": (
|
||||
@@ -1881,6 +1993,7 @@ class RunsClient:
|
||||
"on_disconnect": on_disconnect,
|
||||
"on_completion": on_completion,
|
||||
"after_seconds": after_seconds,
|
||||
"durability": durability,
|
||||
}
|
||||
endpoint = (
|
||||
f"/threads/{thread_id}/runs/stream"
|
||||
@@ -1971,7 +2084,7 @@ class RunsClient:
|
||||
context: Context | None = None,
|
||||
checkpoint: Checkpoint | None = None,
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
checkpoint_during: bool | None = None, # deprecated
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
webhook: str | None = None,
|
||||
@@ -1982,6 +2095,7 @@ class RunsClient:
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
|
||||
durability: Durability | None = None,
|
||||
) -> Run:
|
||||
"""Create a background run.
|
||||
|
||||
@@ -2001,7 +2115,7 @@ class RunsClient:
|
||||
context: Static context to add to the assistant.
|
||||
!!! version-added "Supported with langgraph>=0.6.0"
|
||||
checkpoint: The checkpoint to resume from.
|
||||
checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption).
|
||||
checkpoint_during: (deprecated) Whether to checkpoint during the run (or only at the end/interruption).
|
||||
interrupt_before: Nodes to interrupt immediately before they get executed.
|
||||
interrupt_after: Nodes to Nodes to interrupt immediately after they get executed.
|
||||
webhook: Webhook to call after LangGraph API call is done.
|
||||
@@ -2015,6 +2129,10 @@ class RunsClient:
|
||||
Use to schedule future runs.
|
||||
headers: Optional custom headers to include with the request.
|
||||
on_run_created: Optional callback to call when a run is created.
|
||||
durability: The durability to use for the run. Values are "sync", "async", or "exit".
|
||||
"async" means checkpoints are persisted async while next graph step executes, replaces checkpoint_during=True
|
||||
"sync" means checkpoints are persisted sync after graph step executes, replaces checkpoint_during=False
|
||||
"exit" means checkpoints are only persisted when the run exits, does not save intermediate steps
|
||||
|
||||
Returns:
|
||||
Run: The created background run.
|
||||
@@ -2090,6 +2208,12 @@ class RunsClient:
|
||||
}
|
||||
```
|
||||
""" # noqa: E501
|
||||
if checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed in a future version. Use `durability` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
payload = {
|
||||
"input": input,
|
||||
"command": (
|
||||
@@ -2112,6 +2236,7 @@ class RunsClient:
|
||||
"if_not_exists": if_not_exists,
|
||||
"on_completion": on_completion,
|
||||
"after_seconds": after_seconds,
|
||||
"durability": durability,
|
||||
}
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
@@ -2209,7 +2334,7 @@ class RunsClient:
|
||||
context: Context | None = None,
|
||||
checkpoint: Checkpoint | None = None,
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
checkpoint_during: bool | None = None, # deprecated
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
webhook: str | None = None,
|
||||
@@ -2222,6 +2347,7 @@ class RunsClient:
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
|
||||
durability: Durability | None = None,
|
||||
) -> list[dict] | dict[str, Any]:
|
||||
"""Create a run, wait until it finishes and return the final state.
|
||||
|
||||
@@ -2237,7 +2363,7 @@ class RunsClient:
|
||||
context: Static context to add to the assistant.
|
||||
!!! version-added "Supported with langgraph>=0.6.0"
|
||||
checkpoint: The checkpoint to resume from.
|
||||
checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption).
|
||||
checkpoint_during: (deprecated) Whether to checkpoint during the run (or only at the end/interruption).
|
||||
interrupt_before: Nodes to interrupt immediately before they get executed.
|
||||
interrupt_after: Nodes to Nodes to interrupt immediately after they get executed.
|
||||
webhook: Webhook to call after LangGraph API call is done.
|
||||
@@ -2253,6 +2379,10 @@ class RunsClient:
|
||||
Use to schedule future runs.
|
||||
headers: Optional custom headers to include with the request.
|
||||
on_run_created: Optional callback to call when a run is created.
|
||||
durability: The durability to use for the run. Values are "sync", "async", or "exit".
|
||||
"async" means checkpoints are persisted async while next graph step executes, replaces checkpoint_during=True
|
||||
"sync" means checkpoints are persisted sync after graph step executes, replaces checkpoint_during=False
|
||||
"exit" means checkpoints are only persisted when the run exits, does not save intermediate steps
|
||||
|
||||
Returns:
|
||||
Union[list[dict], dict[str, Any]]: The output of the run.
|
||||
@@ -2306,6 +2436,12 @@ class RunsClient:
|
||||
```
|
||||
|
||||
""" # noqa: E501
|
||||
if checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed in a future version. Use `durability` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
payload = {
|
||||
"input": input,
|
||||
"command": (
|
||||
@@ -2326,6 +2462,7 @@ class RunsClient:
|
||||
"on_disconnect": on_disconnect,
|
||||
"on_completion": on_completion,
|
||||
"after_seconds": after_seconds,
|
||||
"durability": durability,
|
||||
}
|
||||
endpoint = (
|
||||
f"/threads/{thread_id}/runs/wait" if thread_id is not None else "/runs/wait"
|
||||
@@ -4241,6 +4378,7 @@ class SyncThreadsClient:
|
||||
if_exists: OnConflictBehavior | None = None,
|
||||
supersteps: Sequence[dict[str, Sequence[dict[str, Any]]]] | None = None,
|
||||
graph_id: str | None = None,
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -4255,6 +4393,9 @@ class SyncThreadsClient:
|
||||
supersteps: Apply a list of supersteps when creating a thread, each containing a sequence of updates.
|
||||
Each update has `values` or `command` and `as_node`. Used for copying a thread between deployments.
|
||||
graph_id: Optional graph ID to associate with the thread.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
|
||||
Returns:
|
||||
@@ -4296,6 +4437,11 @@ class SyncThreadsClient:
|
||||
}
|
||||
for s in supersteps
|
||||
]
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
|
||||
return self.http.post("/threads", json=payload, headers=headers, params=params)
|
||||
|
||||
@@ -4304,6 +4450,7 @@ class SyncThreadsClient:
|
||||
thread_id: str,
|
||||
*,
|
||||
metadata: Mapping[str, Any],
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -4312,7 +4459,11 @@ class SyncThreadsClient:
|
||||
Args:
|
||||
thread_id: ID of thread to update.
|
||||
metadata: Metadata to merge with existing thread metadata.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
Returns:
|
||||
Thread: The created thread.
|
||||
@@ -4324,12 +4475,19 @@ class SyncThreadsClient:
|
||||
thread = client.threads.update(
|
||||
thread_id="my-thread-id",
|
||||
metadata={"number":1},
|
||||
ttl=43_200,
|
||||
)
|
||||
```
|
||||
""" # noqa: E501
|
||||
payload: dict[str, Any] = {"metadata": metadata}
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
return self.http.patch(
|
||||
f"/threads/{thread_id}",
|
||||
json={"metadata": metadata},
|
||||
json=payload,
|
||||
headers=headers,
|
||||
params=params,
|
||||
)
|
||||
@@ -4367,6 +4525,7 @@ class SyncThreadsClient:
|
||||
*,
|
||||
metadata: Json = None,
|
||||
values: Json = None,
|
||||
ids: Sequence[str] | None = None,
|
||||
status: ThreadStatus | None = None,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
@@ -4381,6 +4540,7 @@ class SyncThreadsClient:
|
||||
Args:
|
||||
metadata: Thread metadata to filter on.
|
||||
values: State values to filter on.
|
||||
ids: List of thread IDs to filter by.
|
||||
status: Thread status to filter on.
|
||||
Must be one of 'idle', 'busy', 'interrupted' or 'error'.
|
||||
limit: Limit on number of threads to return.
|
||||
@@ -4410,6 +4570,8 @@ class SyncThreadsClient:
|
||||
payload["metadata"] = metadata
|
||||
if values:
|
||||
payload["values"] = values
|
||||
if ids:
|
||||
payload["ids"] = ids
|
||||
if status:
|
||||
payload["status"] = status
|
||||
if sort_by:
|
||||
@@ -4733,6 +4895,54 @@ class SyncThreadsClient:
|
||||
params=params,
|
||||
)
|
||||
|
||||
def join_stream(
|
||||
self,
|
||||
thread_id: str,
|
||||
*,
|
||||
stream_mode: ThreadStreamMode | Sequence[ThreadStreamMode] = "run_modes",
|
||||
last_event_id: str | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Iterator[StreamPart]:
|
||||
"""Get a stream of events for a thread.
|
||||
|
||||
Args:
|
||||
thread_id: The ID of the thread to get the stream for.
|
||||
last_event_id: The ID of the last event to get.
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
Returns:
|
||||
Iterator[StreamPart]: An iterator of stream parts.
|
||||
|
||||
???+ example "Example Usage"
|
||||
|
||||
```python
|
||||
|
||||
for chunk in client.threads.join_stream(
|
||||
thread_id="my_thread_id",
|
||||
last_event_id="my_event_id",
|
||||
stream_mode="run_modes",
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
""" # noqa: E501
|
||||
query_params = {
|
||||
"stream_mode": stream_mode,
|
||||
}
|
||||
if params:
|
||||
query_params.update(params)
|
||||
return self.http.stream(
|
||||
f"/threads/{thread_id}/stream",
|
||||
"GET",
|
||||
headers={
|
||||
**({"Last-Event-ID": last_event_id} if last_event_id else {}),
|
||||
**(headers or {}),
|
||||
},
|
||||
params=query_params,
|
||||
)
|
||||
|
||||
|
||||
class SyncRunsClient:
|
||||
"""Synchronous client for managing runs in LangGraph.
|
||||
@@ -4823,7 +5033,7 @@ class SyncRunsClient:
|
||||
context: Context | None = None,
|
||||
checkpoint: Checkpoint | None = None,
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
checkpoint_during: bool | None = None, # deprecated
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
feedback_keys: Sequence[str] | None = None,
|
||||
@@ -4836,6 +5046,7 @@ class SyncRunsClient:
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
|
||||
durability: Durability | None = None,
|
||||
) -> Iterator[StreamPart]:
|
||||
"""Create a run and stream the results.
|
||||
|
||||
@@ -4855,7 +5066,7 @@ class SyncRunsClient:
|
||||
context: Static context to add to the assistant.
|
||||
!!! version-added "Supported with langgraph>=0.6.0"
|
||||
checkpoint: The checkpoint to resume from.
|
||||
checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption).
|
||||
checkpoint_during: (deprecated) Whether to checkpoint during the run (or only at the end/interruption).
|
||||
interrupt_before: Nodes to interrupt immediately before they get executed.
|
||||
interrupt_after: Nodes to Nodes to interrupt immediately after they get executed.
|
||||
feedback_keys: Feedback keys to assign to run.
|
||||
@@ -4872,6 +5083,11 @@ class SyncRunsClient:
|
||||
Use to schedule future runs.
|
||||
headers: Optional custom headers to include with the request.
|
||||
on_run_created: Optional callback to call when a run is created.
|
||||
durability: The durability to use for the run. Values are "sync", "async", or "exit".
|
||||
"async" means checkpoints are persisted async while next graph step executes, replaces checkpoint_during=True
|
||||
"sync" means checkpoints are persisted sync after graph step executes, replaces checkpoint_during=False
|
||||
"exit" means checkpoints are only persisted when the run exits, does not save intermediate steps
|
||||
|
||||
|
||||
Returns:
|
||||
Iterator[StreamPart]: Iterator of stream results.
|
||||
@@ -4904,6 +5120,12 @@ class SyncRunsClient:
|
||||
StreamPart(event='end', data=None)
|
||||
```
|
||||
""" # noqa: E501
|
||||
if checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed in a future version. Use `durability` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
payload = {
|
||||
"input": input,
|
||||
"command": (
|
||||
@@ -4928,6 +5150,7 @@ class SyncRunsClient:
|
||||
"on_disconnect": on_disconnect,
|
||||
"on_completion": on_completion,
|
||||
"after_seconds": after_seconds,
|
||||
"durability": durability,
|
||||
}
|
||||
endpoint = (
|
||||
f"/threads/{thread_id}/runs/stream"
|
||||
@@ -5018,7 +5241,7 @@ class SyncRunsClient:
|
||||
context: Context | None = None,
|
||||
checkpoint: Checkpoint | None = None,
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
checkpoint_during: bool | None = None, # deprecated
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
webhook: str | None = None,
|
||||
@@ -5029,6 +5252,7 @@ class SyncRunsClient:
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
|
||||
durability: Durability | None = None,
|
||||
) -> Run:
|
||||
"""Create a background run.
|
||||
|
||||
@@ -5048,7 +5272,7 @@ class SyncRunsClient:
|
||||
context: Static context to add to the assistant.
|
||||
!!! version-added "Supported with langgraph>=0.6.0"
|
||||
checkpoint: The checkpoint to resume from.
|
||||
checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption).
|
||||
checkpoint_during: (deprecated) Whether to checkpoint during the run (or only at the end/interruption).
|
||||
interrupt_before: Nodes to interrupt immediately before they get executed.
|
||||
interrupt_after: Nodes to Nodes to interrupt immediately after they get executed.
|
||||
webhook: Webhook to call after LangGraph API call is done.
|
||||
@@ -5062,6 +5286,10 @@ class SyncRunsClient:
|
||||
Use to schedule future runs.
|
||||
headers: Optional custom headers to include with the request.
|
||||
on_run_created: Optional callback to call when a run is created.
|
||||
durability: The durability to use for the run. Values are "sync", "async", or "exit".
|
||||
"async" means checkpoints are persisted async while next graph step executes, replaces checkpoint_during=True
|
||||
"sync" means checkpoints are persisted sync after graph step executes, replaces checkpoint_during=False
|
||||
"exit" means checkpoints are only persisted when the run exits, does not save intermediate steps
|
||||
|
||||
Returns:
|
||||
Run: The created background run.
|
||||
@@ -5137,6 +5365,12 @@ class SyncRunsClient:
|
||||
}
|
||||
```
|
||||
""" # noqa: E501
|
||||
if checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed in a future version. Use `durability` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
payload = {
|
||||
"input": input,
|
||||
"command": (
|
||||
@@ -5159,6 +5393,7 @@ class SyncRunsClient:
|
||||
"if_not_exists": if_not_exists,
|
||||
"on_completion": on_completion,
|
||||
"after_seconds": after_seconds,
|
||||
"durability": durability,
|
||||
}
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
@@ -5254,7 +5489,7 @@ class SyncRunsClient:
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
context: Context | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
checkpoint_during: bool | None = None, # deprecated
|
||||
checkpoint: Checkpoint | None = None,
|
||||
checkpoint_id: str | None = None,
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
@@ -5269,6 +5504,7 @@ class SyncRunsClient:
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
on_run_created: Callable[[RunCreateMetadata], None] | None = None,
|
||||
durability: Durability | None = None,
|
||||
) -> list[dict] | dict[str, Any]:
|
||||
"""Create a run, wait until it finishes and return the final state.
|
||||
|
||||
@@ -5284,7 +5520,7 @@ class SyncRunsClient:
|
||||
context: Static context to add to the assistant.
|
||||
!!! version-added "Supported with langgraph>=0.6.0"
|
||||
checkpoint: The checkpoint to resume from.
|
||||
checkpoint_during: Whether to checkpoint during the run (or only at the end/interruption).
|
||||
checkpoint_during: (deprecated) Whether to checkpoint during the run (or only at the end/interruption).
|
||||
interrupt_before: Nodes to interrupt immediately before they get executed.
|
||||
interrupt_after: Nodes to Nodes to interrupt immediately after they get executed.
|
||||
webhook: Webhook to call after LangGraph API call is done.
|
||||
@@ -5301,6 +5537,10 @@ class SyncRunsClient:
|
||||
raise_error: Whether to raise an error if the run fails.
|
||||
headers: Optional custom headers to include with the request.
|
||||
on_run_created: Optional callback to call when a run is created.
|
||||
durability: The durability to use for the run. Values are "sync", "async", or "exit".
|
||||
"async" means checkpoints are persisted async while next graph step executes, replaces checkpoint_during=True
|
||||
"sync" means checkpoints are persisted sync after graph step executes, replaces checkpoint_during=False
|
||||
"exit" means checkpoints are only persisted when the run exits, does not save intermediate steps
|
||||
|
||||
Returns:
|
||||
Union[list[dict], dict[str, Any]]: The output of the run.
|
||||
@@ -5355,6 +5595,12 @@ class SyncRunsClient:
|
||||
```
|
||||
|
||||
""" # noqa: E501
|
||||
if checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed in a future version. Use `durability` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
payload = {
|
||||
"input": input,
|
||||
"command": (
|
||||
@@ -5376,6 +5622,7 @@ class SyncRunsClient:
|
||||
"on_completion": on_completion,
|
||||
"after_seconds": after_seconds,
|
||||
"raise_error": raise_error,
|
||||
"durability": durability,
|
||||
}
|
||||
|
||||
def on_response(res: httpx.Response):
|
||||
|
||||
@@ -38,6 +38,14 @@ Represents the status of a thread:
|
||||
- "error": An exception occurred during task processing.
|
||||
"""
|
||||
|
||||
ThreadStreamMode = Literal["run_modes", "lifecycle", "state_update"]
|
||||
"""
|
||||
Defines the mode of streaming:
|
||||
- "run_modes": Stream the same events as the runs on thread, as well as run_done events.
|
||||
- "lifecycle": Stream only run start/end events.
|
||||
- "state_update": Stream state updates on the thread.
|
||||
"""
|
||||
|
||||
StreamMode = Literal[
|
||||
"values",
|
||||
"messages",
|
||||
@@ -91,6 +99,12 @@ Defines action after completion:
|
||||
- "keep": Retain resources after completion.
|
||||
"""
|
||||
|
||||
Durability = Literal["sync", "async", "exit"]
|
||||
"""Durability mode for the graph execution.
|
||||
- `"sync"`: Changes are persisted synchronously before the next step starts.
|
||||
- `"async"`: Changes are persisted asynchronously while the next step executes.
|
||||
- `"exit"`: Changes are persisted only when the graph exits."""
|
||||
|
||||
All = Literal["*"]
|
||||
"""Represents a wildcard or 'all' selector."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user