mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-28 10:49:56 +02:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cae6b8f9db | ||
|
|
f8c1a323cc | ||
|
|
b7e329cf0a | ||
|
|
3f49249e72 | ||
|
|
78910dade0 | ||
|
|
33fbd1bb9c | ||
|
|
d45f52ab0c |
@@ -32,7 +32,7 @@ jobs:
|
||||
run:
|
||||
working-directory: libs/cli
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
- "3.12"
|
||||
name: "lint #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
working-directory: libs/langgraph
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
version: ${{ steps.check-version.outputs.version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python $${ env.PYTHON_VERSION }}
|
||||
uses: astral-sh/setup-uv@v7
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
run:
|
||||
working-directory: libs/langgraph
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- run: SHA=$(git rev-parse HEAD) && echo "SHA=$SHA" >> $GITHUB_ENV
|
||||
- name: Set up Python 3.11
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
run:
|
||||
working-directory: libs/langgraph
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- 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@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
name: "Check SDK methods matching"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
python-version:
|
||||
- "3.11"
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.MKDOCS_GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
tag: ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v7
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
outputs:
|
||||
release-body: ${{ steps.generate-release-body.outputs.release-body }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
repository: langchain-ai/langgraph
|
||||
path: langgraph
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
- test-pypi-publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# We explicitly *don't* set up caching here. This ensures our tests are
|
||||
# maximally sensitive to catching breakage.
|
||||
@@ -260,7 +260,7 @@ jobs:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v7
|
||||
@@ -301,7 +301,7 @@ jobs:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- "latest"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python + Poetry
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
@@ -4698,6 +4698,11 @@
|
||||
"title": "Graph Id",
|
||||
"description": "The ID of the graph to filter by. The graph ID is normally set in your langgraph.json configuration."
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name",
|
||||
"description": "Name of the assistant to filter by. The filtering logic will match (case insensitive) assistants where 'name' is a substring of the assistant name."
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"title": "Limit",
|
||||
|
||||
@@ -26,7 +26,6 @@ docs = [
|
||||
"langgraph-checkpoint-postgres",
|
||||
"langgraph-sdk",
|
||||
"langgraph-supervisor",
|
||||
"langgraph-swarm",
|
||||
"langchain-mcp-adapters",
|
||||
"langchain-ollama",
|
||||
"mkdocs",
|
||||
|
||||
Generated
+3688
-2491
File diff suppressed because it is too large
Load Diff
@@ -60,23 +60,28 @@ class Checkpoint(TypedDict):
|
||||
"""State snapshot at a given point in time."""
|
||||
|
||||
v: int
|
||||
"""The version of the checkpoint format. Currently 1."""
|
||||
"""The version of the checkpoint format. Currently `1`."""
|
||||
id: str
|
||||
"""The ID of the checkpoint. This is both unique and monotonically
|
||||
increasing, so can be used for sorting checkpoints from first to last."""
|
||||
"""The ID of the checkpoint.
|
||||
|
||||
This is both unique and monotonically increasing, so can be used for sorting
|
||||
checkpoints from first to last."""
|
||||
ts: str
|
||||
"""The timestamp of the checkpoint in ISO 8601 format."""
|
||||
channel_values: dict[str, Any]
|
||||
"""The values of the channels at the time of the checkpoint.
|
||||
|
||||
Mapping from channel name to deserialized channel snapshot value.
|
||||
"""
|
||||
channel_versions: ChannelVersions
|
||||
"""The versions of the channels at the time of the checkpoint.
|
||||
|
||||
The keys are channel names and the values are monotonically increasing
|
||||
version strings for each channel.
|
||||
"""
|
||||
versions_seen: dict[str, ChannelVersions]
|
||||
"""Map from node ID to map from channel name to version seen.
|
||||
|
||||
This keeps track of the versions of the channels that each node has seen.
|
||||
Used to determine which nodes to execute next.
|
||||
"""
|
||||
@@ -352,8 +357,9 @@ class BaseCheckpointSaver(Generic[V]):
|
||||
def get_next_version(self, current: V | None, channel: None) -> V:
|
||||
"""Generate the next version ID for a channel.
|
||||
|
||||
Default is to use integer versions, incrementing by `1`. If you override, you can use `str`/`int`/`float`
|
||||
versions, as long as they are monotonically increasing.
|
||||
Default is to use integer versions, incrementing by `1`.
|
||||
|
||||
If you override, you can use `str`/`int`/`float` versions, as long as they are monotonically increasing.
|
||||
|
||||
Args:
|
||||
current: The current version identifier (`int`, `float`, or `str`).
|
||||
|
||||
@@ -33,7 +33,7 @@ class InMemorySaver(
|
||||
):
|
||||
"""An in-memory checkpoint saver.
|
||||
|
||||
This checkpoint saver stores checkpoints in memory using a defaultdict.
|
||||
This checkpoint saver stores checkpoints in memory using a `defaultdict`.
|
||||
|
||||
Note:
|
||||
Only use `InMemorySaver` for debugging or testing purposes.
|
||||
@@ -44,22 +44,23 @@ class InMemorySaver(
|
||||
Args:
|
||||
serde: The serializer to use for serializing and deserializing checkpoints.
|
||||
|
||||
Examples:
|
||||
Example:
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
import asyncio
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.graph import StateGraph
|
||||
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.graph import StateGraph
|
||||
builder = StateGraph(int)
|
||||
builder.add_node("add_one", lambda x: x + 1)
|
||||
builder.set_entry_point("add_one")
|
||||
builder.set_finish_point("add_one")
|
||||
|
||||
builder = StateGraph(int)
|
||||
builder.add_node("add_one", lambda x: x + 1)
|
||||
builder.set_entry_point("add_one")
|
||||
builder.set_finish_point("add_one")
|
||||
|
||||
memory = InMemorySaver()
|
||||
graph = builder.compile(checkpointer=memory)
|
||||
coro = graph.ainvoke(1, {"configurable": {"thread_id": "thread-1"}})
|
||||
asyncio.run(coro) # Output: 2
|
||||
memory = InMemorySaver()
|
||||
graph = builder.compile(checkpointer=memory)
|
||||
coro = graph.ainvoke(1, {"configurable": {"thread_id": "thread-1"}})
|
||||
asyncio.run(coro) # Output: 2
|
||||
```
|
||||
"""
|
||||
|
||||
# thread ID -> checkpoint NS -> checkpoint ID -> checkpoint mapping
|
||||
|
||||
@@ -52,12 +52,13 @@ def maybe_add_typed_methods(
|
||||
|
||||
class CipherProtocol(Protocol):
|
||||
"""Protocol for encryption and decryption of data.
|
||||
|
||||
- `encrypt`: Encrypt plaintext.
|
||||
- `decrypt`: Decrypt ciphertext.
|
||||
"""
|
||||
|
||||
def encrypt(self, plaintext: bytes) -> tuple[str, bytes]:
|
||||
"""Encrypt plaintext. Returns a tuple (cipher name, ciphertext)."""
|
||||
"""Encrypt plaintext. Returns a tuple `(cipher name, ciphertext)`."""
|
||||
...
|
||||
|
||||
def decrypt(self, ciphername: str, ciphertext: bytes) -> bytes:
|
||||
|
||||
@@ -41,10 +41,12 @@ logger = logging.getLogger(__name__)
|
||||
class JsonPlusSerializer(SerializerProtocol):
|
||||
"""Serializer that uses ormsgpack, with optional fallbacks.
|
||||
|
||||
Security note: this serializer is intended for use within the BaseCheckpointSaver
|
||||
class and called within the Pregel loop. It should not be used on untrusted
|
||||
python objects. If an attacker can write directly to your checkpoint database,
|
||||
they may be able to trigger code execution when data is deserialized.
|
||||
!!! warning
|
||||
|
||||
Security note: This serializer is intended for use within the `BaseCheckpointSaver`
|
||||
class and called within the Pregel loop. It should not be used on untrusted
|
||||
python objects. If an attacker can write directly to your checkpoint database,
|
||||
they may be able to trigger code execution when data is deserialized.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -39,14 +39,19 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
|
||||
|
||||
def copy(self) -> Self:
|
||||
"""Return a copy of the channel.
|
||||
|
||||
By default, delegates to `checkpoint()` and `from_checkpoint()`.
|
||||
Subclasses can override this method with a more efficient implementation."""
|
||||
|
||||
Subclasses can override this method with a more efficient implementation.
|
||||
"""
|
||||
return self.from_checkpoint(self.checkpoint())
|
||||
|
||||
def checkpoint(self) -> Checkpoint | Any:
|
||||
"""Return a serializable representation of the channel's current state.
|
||||
|
||||
Raises `EmptyChannelError` if the channel is empty (never updated yet),
|
||||
or doesn't support checkpoints."""
|
||||
or doesn't support checkpoints.
|
||||
"""
|
||||
try:
|
||||
return self.get()
|
||||
except EmptyChannelError:
|
||||
@@ -55,7 +60,9 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
|
||||
@abstractmethod
|
||||
def from_checkpoint(self, checkpoint: Checkpoint | Any) -> Self:
|
||||
"""Return a new identical channel, optionally initialized from a checkpoint.
|
||||
If the checkpoint contains complex data structures, they should be copied."""
|
||||
|
||||
If the checkpoint contains complex data structures, they should be copied.
|
||||
"""
|
||||
|
||||
# read methods
|
||||
|
||||
@@ -67,6 +74,7 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
|
||||
|
||||
def is_available(self) -> bool:
|
||||
"""Return `True` if the channel is available (not empty), `False` otherwise.
|
||||
|
||||
Subclasses should override this method to provide a more efficient
|
||||
implementation than calling `get()` and catching `EmptyChannelError`.
|
||||
"""
|
||||
@@ -83,21 +91,29 @@ class BaseChannel(Generic[Value, Update, Checkpoint], ABC):
|
||||
"""Update the channel's value with the given sequence of updates.
|
||||
The order of the updates in the sequence is arbitrary.
|
||||
This method is called by Pregel for all channels at the end of each step.
|
||||
|
||||
If there are no updates, it is called with an empty sequence.
|
||||
|
||||
Raises `InvalidUpdateError` if the sequence of updates is invalid.
|
||||
|
||||
Returns `True` if the channel was updated, `False` otherwise."""
|
||||
|
||||
def consume(self) -> bool:
|
||||
"""Notify the channel that a subscribed task ran. By default, no-op.
|
||||
A channel can use this method to modify its state, preventing the value
|
||||
from being consumed again.
|
||||
"""Notify the channel that a subscribed task ran.
|
||||
|
||||
By default, no-op.
|
||||
|
||||
A channel can use this method to modify its state, preventing the value from being consumed again.
|
||||
|
||||
Returns `True` if the channel was updated, `False` otherwise.
|
||||
"""
|
||||
return False
|
||||
|
||||
def finish(self) -> bool:
|
||||
"""Notify the channel that the Pregel run is finishing. By default, no-op.
|
||||
"""Notify the channel that the Pregel run is finishing.
|
||||
|
||||
By default, no-op.
|
||||
|
||||
A channel can use this method to modify its state, preventing finish.
|
||||
|
||||
Returns `True` if the channel was updated, `False` otherwise.
|
||||
|
||||
@@ -32,8 +32,8 @@ def get_config() -> RunnableConfig:
|
||||
def get_store() -> BaseStore:
|
||||
"""Access LangGraph store from inside a graph node or entrypoint task at runtime.
|
||||
|
||||
Can be called from inside any [StateGraph][langgraph.graph.StateGraph] node or
|
||||
functional API [task][langgraph.func.task], as long as the StateGraph or the [entrypoint][langgraph.func.entrypoint]
|
||||
Can be called from inside any [`StateGraph`][langgraph.graph.StateGraph] node or
|
||||
functional API [`task`][langgraph.func.task], as long as the `StateGraph` or the [`entrypoint`][langgraph.func.entrypoint]
|
||||
was initialized with a store, e.g.:
|
||||
|
||||
```python
|
||||
@@ -53,10 +53,10 @@ def get_store() -> BaseStore:
|
||||
!!! warning "Async with Python < 3.11"
|
||||
|
||||
If you are using Python < 3.11 and are running LangGraph asynchronously,
|
||||
`get_store()` won't work since it uses [contextvar](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
|
||||
`get_store()` won't work since it uses [`contextvar`](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
|
||||
|
||||
|
||||
Example: Using with StateGraph
|
||||
Example: Using with `StateGraph`
|
||||
```python
|
||||
from typing_extensions import TypedDict
|
||||
from langgraph.graph import StateGraph, START
|
||||
@@ -124,17 +124,17 @@ def get_store() -> BaseStore:
|
||||
|
||||
|
||||
def get_stream_writer() -> StreamWriter:
|
||||
"""Access LangGraph [StreamWriter][langgraph.types.StreamWriter] from inside a graph node or entrypoint task at runtime.
|
||||
"""Access LangGraph [`StreamWriter`][langgraph.types.StreamWriter] from inside a graph node or entrypoint task at runtime.
|
||||
|
||||
Can be called from inside any [StateGraph][langgraph.graph.StateGraph] node or
|
||||
functional API [task][langgraph.func.task].
|
||||
Can be called from inside any [`StateGraph`][langgraph.graph.StateGraph] node or
|
||||
functional API [`task`][langgraph.func.task].
|
||||
|
||||
!!! warning "Async with Python < 3.11"
|
||||
|
||||
If you are using Python < 3.11 and are running LangGraph asynchronously,
|
||||
`get_stream_writer()` won't work since it uses [contextvar](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
|
||||
`get_stream_writer()` won't work since it uses [`contextvar`](https://docs.python.org/3/library/contextvars.html) propagation (only available in [Python >= 3.11](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)).
|
||||
|
||||
Example: Using with StateGraph
|
||||
Example: Using with `StateGraph`
|
||||
```python
|
||||
from typing_extensions import TypedDict
|
||||
from langgraph.graph import StateGraph, START
|
||||
|
||||
@@ -357,7 +357,7 @@ class entrypoint(Generic[ContextT]):
|
||||
my_workflow.invoke("hello", config)
|
||||
```
|
||||
|
||||
Example: Using entrypoint.final to save a value
|
||||
Example: Using `entrypoint.final` to save a value
|
||||
The `entrypoint.final` object allows you to return a value while saving
|
||||
a different value to the checkpoint. This value will be accessible
|
||||
in the next invocation of the entrypoint via the `previous` parameter, as
|
||||
|
||||
@@ -88,8 +88,8 @@ def add_messages(
|
||||
If a message in `right` has the same ID as a message in `left`, the
|
||||
message from `right` will replace the message from `left`.
|
||||
|
||||
Example:
|
||||
```python title="Basic usage"
|
||||
Example: Basic usage
|
||||
```python
|
||||
from langchain_core.messages import AIMessage, HumanMessage
|
||||
|
||||
msgs1 = [HumanMessage(content="Hello", id="1")]
|
||||
@@ -98,14 +98,16 @@ def add_messages(
|
||||
# [HumanMessage(content='Hello', id='1'), AIMessage(content='Hi there!', id='2')]
|
||||
```
|
||||
|
||||
```python title="Overwrite existing message"
|
||||
Example: Overwrite existing message
|
||||
```python
|
||||
msgs1 = [HumanMessage(content="Hello", id="1")]
|
||||
msgs2 = [HumanMessage(content="Hello again", id="1")]
|
||||
add_messages(msgs1, msgs2)
|
||||
# [HumanMessage(content='Hello again', id='1')]
|
||||
```
|
||||
|
||||
```python title="Use in a StateGraph"
|
||||
Example: Use in a StateGraph
|
||||
```python
|
||||
from typing import Annotated
|
||||
from typing_extensions import TypedDict
|
||||
from langgraph.graph import StateGraph
|
||||
@@ -124,7 +126,8 @@ def add_messages(
|
||||
# {'messages': [AIMessage(content='Hello', id=...)]}
|
||||
```
|
||||
|
||||
```python title="Use OpenAI message format"
|
||||
Example: Use OpenAI message format
|
||||
```python
|
||||
from typing import Annotated
|
||||
from typing_extensions import TypedDict
|
||||
from langgraph.graph import StateGraph, add_messages
|
||||
|
||||
@@ -127,6 +127,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
Args:
|
||||
state_schema: The schema class that defines the state.
|
||||
context_schema: The schema class that defines the runtime context.
|
||||
|
||||
Use this to expose immutable context data to your nodes, like `user_id`, `db_conn`, etc.
|
||||
input_schema: The schema class that defines the input to the graph.
|
||||
output_schema: The schema class that defines the output from the graph.
|
||||
@@ -371,18 +372,23 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
|
||||
Args:
|
||||
node: The function or runnable this node will run.
|
||||
|
||||
If a string is provided, it will be used as the node name, and action will be used as the function or runnable.
|
||||
action: The action associated with the node.
|
||||
Will be used as the node function or runnable if `node` is a string (node name).
|
||||
defer: Whether to defer the execution of the node until the run is about to end.
|
||||
metadata: The metadata associated with the node.
|
||||
input_schema: The input schema for the node. (default: the graph's state schema)
|
||||
input_schema: The input schema for the node. (Default: the graph's state schema)
|
||||
retry_policy: The retry policy for the node.
|
||||
|
||||
If a sequence is provided, the first matching policy will be applied.
|
||||
cache_policy: The cache policy for the node.
|
||||
destinations: Destinations that indicate where a node can route to.
|
||||
This is useful for edgeless graphs with nodes that return `Command` objects.
|
||||
|
||||
Useful for edgeless graphs with nodes that return `Command` objects.
|
||||
|
||||
If a `dict` is provided, the keys will be used as the target node names and the values will be used as the labels for the edges.
|
||||
|
||||
If a `tuple` is provided, the values will be used as the target node names.
|
||||
|
||||
!!! note
|
||||
@@ -631,11 +637,14 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
Args:
|
||||
source: The starting node. This conditional edge will run when
|
||||
exiting this node.
|
||||
path: The callable that determines the next
|
||||
node or nodes. If not specifying `path_map` it should return one or
|
||||
more nodes. If it returns `'END'`, the graph will stop execution.
|
||||
path_map: Optional mapping of paths to node
|
||||
names. If omitted the paths returned by `path` should be node names.
|
||||
path: The callable that determines the next node or nodes.
|
||||
|
||||
If not specifying `path_map` it should return one or more nodes.
|
||||
|
||||
If it returns `'END'`, the graph will stop execution.
|
||||
path_map: Optional mapping of paths to node names.
|
||||
|
||||
If omitted the paths returned by `path` should be node names.
|
||||
|
||||
Returns:
|
||||
Self: The instance of the graph, allowing for method chaining.
|
||||
@@ -676,7 +685,9 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
|
||||
Args:
|
||||
nodes: A sequence of `StateNode` (callables that accept a `state` arg) or `(name, StateNode)` tuples.
|
||||
|
||||
If no names are provided, the name will be inferred from the node object (e.g. a `Runnable` or a `Callable` name).
|
||||
|
||||
Each node will be executed in the order provided.
|
||||
|
||||
Raises:
|
||||
@@ -733,11 +744,14 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
"""Sets a conditional entry point in the graph.
|
||||
|
||||
Args:
|
||||
path: The callable that determines the next
|
||||
node or nodes. If not specifying `path_map` it should return one or
|
||||
more nodes. If it returns END, the graph will stop execution.
|
||||
path_map: Optional mapping of paths to node
|
||||
names. If omitted the paths returned by `path` should be node names.
|
||||
path: The callable that determines the next node or nodes.
|
||||
|
||||
If not specifying `path_map` it should return one or more nodes.
|
||||
|
||||
If it returns END, the graph will stop execution.
|
||||
path_map: Optional mapping of paths to node names.
|
||||
|
||||
If omitted the paths returned by `path` should be node names.
|
||||
|
||||
Returns:
|
||||
Self: The instance of the graph, allowing for method chaining.
|
||||
@@ -824,9 +838,12 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
|
||||
Args:
|
||||
checkpointer: A checkpoint saver object or flag.
|
||||
|
||||
If provided, this `Checkpointer` serves as a fully versioned "short-term memory" for the graph,
|
||||
allowing it to be paused, resumed, and replayed from any point.
|
||||
|
||||
If `None`, it may inherit the parent graph's checkpointer when used as a subgraph.
|
||||
|
||||
If `False`, it will not use or inherit any checkpointer.
|
||||
interrupt_before: An optional list of node names to interrupt before.
|
||||
interrupt_after: An optional list of node names to interrupt after.
|
||||
|
||||
@@ -210,8 +210,10 @@ class NodeBuilder:
|
||||
*channels: str,
|
||||
read: bool = True,
|
||||
) -> Self:
|
||||
"""Add channels to subscribe to. Node will be invoked when any of these
|
||||
channels are updated, with a dict of the channel values as input.
|
||||
"""Add channels to subscribe to.
|
||||
|
||||
Node will be invoked when any of these channels are updated, with a dict of the
|
||||
channel values as input.
|
||||
|
||||
Args:
|
||||
channels: Channel name(s) to subscribe to
|
||||
@@ -270,8 +272,8 @@ class NodeBuilder:
|
||||
"""Add channel writes.
|
||||
|
||||
Args:
|
||||
*channels: Channel names to write to
|
||||
**kwargs: Channel name and value mappings
|
||||
*channels: Channel names to write to.
|
||||
**kwargs: Channel name and value mappings.
|
||||
|
||||
Returns:
|
||||
Self for chaining
|
||||
@@ -375,12 +377,12 @@ class Pregel(
|
||||
### Advanced channels: Context and BinaryOperatorAggregate
|
||||
|
||||
- `Context`: exposes the value of a context manager, managing its lifecycle.
|
||||
Useful for accessing external resources that require setup and/or teardown. eg.
|
||||
`client = Context(httpx.Client)`
|
||||
Useful for accessing external resources that require setup and/or teardown. e.g.
|
||||
`client = Context(httpx.Client)`
|
||||
- `BinaryOperatorAggregate`: stores a persistent value, updated by applying
|
||||
a binary operator to the current value and each update
|
||||
sent to the channel, useful for computing aggregates over multiple steps. eg.
|
||||
`total = BinaryOperatorAggregate(int, operator.add)`
|
||||
a binary operator to the current value and each update
|
||||
sent to the channel, useful for computing aggregates over multiple steps. e.g.
|
||||
`total = BinaryOperatorAggregate(int, operator.add)`
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -495,7 +497,7 @@ class Pregel(
|
||||
{"c": ["foofoo", "foofoofoofoo"]}
|
||||
```
|
||||
|
||||
Example: Using a BinaryOperatorAggregate channel
|
||||
Example: Using a `BinaryOperatorAggregate` channel
|
||||
```python
|
||||
from langgraph.channels import EphemeralValue, BinaryOperatorAggregate
|
||||
from langgraph.pregel import Pregel, NodeBuilder
|
||||
@@ -541,8 +543,9 @@ class Pregel(
|
||||
|
||||
Example: Introducing a cycle
|
||||
This example demonstrates how to introduce a cycle in the graph, by having
|
||||
a chain write to a channel it subscribes to. Execution will continue
|
||||
until a None value is written to the channel.
|
||||
a chain write to a channel it subscribes to.
|
||||
|
||||
Execution will continue until a `None` value is written to the channel.
|
||||
|
||||
```python
|
||||
from langgraph.channels import EphemeralValue
|
||||
@@ -1426,7 +1429,8 @@ class Pregel(
|
||||
Args:
|
||||
config: The config to apply the updates to.
|
||||
supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state.
|
||||
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
|
||||
|
||||
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
|
||||
|
||||
Raises:
|
||||
ValueError: If no checkpointer is set or no updates are provided.
|
||||
@@ -1869,7 +1873,8 @@ class Pregel(
|
||||
Args:
|
||||
config: The config to apply the updates to.
|
||||
supersteps: A list of supersteps, each including a list of updates to apply sequentially to a graph state.
|
||||
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
|
||||
|
||||
Each update is a tuple of the form `(values, as_node, task_id)` where `task_id` is optional.
|
||||
|
||||
Raises:
|
||||
ValueError: If no checkpointer is set or no updates are provided.
|
||||
@@ -2420,6 +2425,7 @@ class Pregel(
|
||||
context: The static context to use for the run.
|
||||
!!! version-added "Added in version 0.6.0"
|
||||
stream_mode: The mode to stream output, defaults to `self.stream_mode`.
|
||||
|
||||
Options are:
|
||||
|
||||
- `"values"`: Emit all values in the state after each step, including interrupts.
|
||||
@@ -2428,7 +2434,7 @@ class Pregel(
|
||||
If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately.
|
||||
- `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`.
|
||||
- `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks.
|
||||
Will be emitted as 2-tuples `(LLM token, metadata)`.
|
||||
- Will be emitted as 2-tuples `(LLM token, metadata)`.
|
||||
- `"checkpoints"`: Emit an event when a checkpoint is created, in the same format as returned by `get_state()`.
|
||||
- `"tasks"`: Emit events when tasks start and finish, including their results and errors.
|
||||
- `"debug"`: Emit debug events with as much information as possible for each step.
|
||||
@@ -2437,17 +2443,21 @@ class Pregel(
|
||||
The streamed outputs will be tuples of `(mode, data)`.
|
||||
|
||||
See [LangGraph streaming guide](https://docs.langchain.com/oss/python/langgraph/streaming) for more details.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
|
||||
|
||||
Does not affect the output of the graph in any way.
|
||||
output_keys: The keys to stream, defaults to all non-context channels.
|
||||
interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph.
|
||||
interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph.
|
||||
durability: The durability mode for the graph execution, defaults to `"async"`.
|
||||
|
||||
Options are:
|
||||
|
||||
- `"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.
|
||||
subgraphs: Whether to stream events from inside subgraphs, defaults to False.
|
||||
subgraphs: Whether to stream events from inside subgraphs, defaults to `False`.
|
||||
|
||||
If `True`, the events will be emitted as tuples `(namespace, data)`,
|
||||
or `(namespace, mode, data)` if `stream_mode` is a list,
|
||||
where `namespace` is a tuple with the path to the node where a subgraph is invoked,
|
||||
@@ -2689,6 +2699,7 @@ class Pregel(
|
||||
context: The static context to use for the run.
|
||||
!!! version-added "Added in version 0.6.0"
|
||||
stream_mode: The mode to stream output, defaults to `self.stream_mode`.
|
||||
|
||||
Options are:
|
||||
|
||||
- `"values"`: Emit all values in the state after each step, including interrupts.
|
||||
@@ -2697,7 +2708,7 @@ class Pregel(
|
||||
If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are emitted separately.
|
||||
- `"custom"`: Emit custom data from inside nodes or tasks using `StreamWriter`.
|
||||
- `"messages"`: Emit LLM messages token-by-token together with metadata for any LLM invocations inside nodes or tasks.
|
||||
Will be emitted as 2-tuples `(LLM token, metadata)`.
|
||||
- Will be emitted as 2-tuples `(LLM token, metadata)`.
|
||||
- `"checkpoints"`: Emit an event when a checkpoint is created, in the same format as returned by `get_state()`.
|
||||
- `"tasks"`: Emit events when tasks start and finish, including their results and errors.
|
||||
- `"debug"`: Emit debug events with as much information as possible for each step.
|
||||
@@ -2706,17 +2717,21 @@ class Pregel(
|
||||
The streamed outputs will be tuples of `(mode, data)`.
|
||||
|
||||
See [LangGraph streaming guide](https://docs.langchain.com/oss/python/langgraph/streaming) for more details.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
|
||||
|
||||
Does not affect the output of the graph in any way.
|
||||
output_keys: The keys to stream, defaults to all non-context channels.
|
||||
interrupt_before: Nodes to interrupt before, defaults to all nodes in the graph.
|
||||
interrupt_after: Nodes to interrupt after, defaults to all nodes in the graph.
|
||||
durability: The durability mode for the graph execution, defaults to `"async"`.
|
||||
|
||||
Options are:
|
||||
|
||||
- `"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.
|
||||
subgraphs: Whether to stream events from inside subgraphs, defaults to False.
|
||||
subgraphs: Whether to stream events from inside subgraphs, defaults to `False`.
|
||||
|
||||
If `True`, the events will be emitted as tuples `(namespace, data)`,
|
||||
or `(namespace, mode, data)` if `stream_mode` is a list,
|
||||
where `namespace` is a tuple with the path to the node where a subgraph is invoked,
|
||||
@@ -3025,11 +3040,14 @@ class Pregel(
|
||||
context: The static context to use for the run.
|
||||
!!! version-added "Added in version 0.6.0"
|
||||
stream_mode: The stream mode for the graph run.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
|
||||
|
||||
Does not affect the output of the graph in any way.
|
||||
output_keys: The output keys to retrieve from the graph run.
|
||||
interrupt_before: The nodes to interrupt the graph run before.
|
||||
interrupt_after: The nodes to interrupt the graph run after.
|
||||
durability: The durability mode for the graph execution, defaults to `"async"`.
|
||||
|
||||
Options are:
|
||||
|
||||
- `"sync"`: Changes are persisted synchronously before the next step starts.
|
||||
@@ -3112,11 +3130,14 @@ class Pregel(
|
||||
context: The static context to use for the run.
|
||||
!!! version-added "Added in version 0.6.0"
|
||||
stream_mode: The stream mode for the graph run.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes. Does not affect the output of the graph in any way.
|
||||
print_mode: Accepts the same values as `stream_mode`, but only prints the output to the console, for debugging purposes.
|
||||
|
||||
Does not affect the output of the graph in any way.
|
||||
output_keys: The output keys to retrieve from the graph run.
|
||||
interrupt_before: The nodes to interrupt the graph run before.
|
||||
interrupt_after: The nodes to interrupt the graph run after.
|
||||
durability: The durability mode for the graph execution, defaults to `"async"`.
|
||||
|
||||
Options are:
|
||||
|
||||
- `"sync"`: Changes are persisted synchronously before the next step starts.
|
||||
|
||||
@@ -725,9 +725,10 @@ class RemoteGraph(PregelProtocol):
|
||||
input = None
|
||||
else:
|
||||
command = None
|
||||
thread_id = sanitized_config.get("configurable", {}).pop("thread_id", None)
|
||||
|
||||
for chunk in sync_client.runs.stream(
|
||||
thread_id=sanitized_config["configurable"].get("thread_id"),
|
||||
thread_id=thread_id,
|
||||
assistant_id=self.assistant_id,
|
||||
input=input,
|
||||
command=command,
|
||||
@@ -834,9 +835,10 @@ class RemoteGraph(PregelProtocol):
|
||||
input = None
|
||||
else:
|
||||
command = None
|
||||
thread_id = sanitized_config.get("configurable", {}).pop("thread_id", None)
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id=sanitized_config["configurable"].get("thread_id"),
|
||||
thread_id=thread_id,
|
||||
assistant_id=self.assistant_id,
|
||||
input=input,
|
||||
command=command,
|
||||
|
||||
@@ -840,6 +840,46 @@ def test_invoke():
|
||||
assert result == {"messages": [{"type": "human", "content": "world"}]}
|
||||
|
||||
|
||||
def test_invoke_sanitizes_thread_id():
|
||||
# Ensure that invoking with thread_id passes thread_id as a top-level arg
|
||||
# and removes it from the config body.
|
||||
mock_sync_client = MagicMock()
|
||||
mock_sync_client.runs.stream.return_value = []
|
||||
remote_pregel = RemoteGraph("test_graph_id", sync_client=mock_sync_client)
|
||||
|
||||
config = {"configurable": {"thread_id": "thread_1"}}
|
||||
remote_pregel.invoke(
|
||||
{"input": {"messages": [{"type": "human", "content": "hello"}]}}, config
|
||||
)
|
||||
|
||||
assert mock_sync_client.runs.stream.called
|
||||
_, kwargs = mock_sync_client.runs.stream.call_args
|
||||
assert kwargs.get("thread_id") == "thread_1"
|
||||
passed_config = kwargs.get("config") or {}
|
||||
assert "configurable" in passed_config
|
||||
assert "thread_id" not in passed_config["configurable"]
|
||||
assert not passed_config["configurable"]
|
||||
|
||||
|
||||
def test_stream_sanitizes_thread_id():
|
||||
# Ensure that streaming with thread_id passes thread_id as a top-level arg
|
||||
# and removes it from the config body.
|
||||
mock_sync_client = MagicMock()
|
||||
mock_sync_client.runs.stream.return_value = []
|
||||
remote_pregel = RemoteGraph("test_graph_id", sync_client=mock_sync_client)
|
||||
|
||||
config = {"configurable": {"thread_id": "thread_2"}}
|
||||
list(remote_pregel.stream({"input": {"messages": []}}, config))
|
||||
|
||||
assert mock_sync_client.runs.stream.called
|
||||
_, kwargs = mock_sync_client.runs.stream.call_args
|
||||
assert kwargs.get("thread_id") == "thread_2"
|
||||
passed_config = kwargs.get("config") or {}
|
||||
assert "configurable" in passed_config
|
||||
assert "thread_id" not in passed_config["configurable"]
|
||||
assert not passed_config["configurable"]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_ainvoke():
|
||||
# set up test
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from langgraph_sdk.auth import Auth
|
||||
from langgraph_sdk.client import get_client, get_sync_client
|
||||
|
||||
__version__ = "0.2.9"
|
||||
__version__ = "0.2.10"
|
||||
|
||||
__all__ = ["Auth", "get_client", "get_sync_client"]
|
||||
|
||||
@@ -48,6 +48,7 @@ from langgraph_sdk.schema import (
|
||||
Durability,
|
||||
GraphSchema,
|
||||
IfNotExists,
|
||||
Input,
|
||||
Item,
|
||||
Json,
|
||||
ListNamespaceResponse,
|
||||
@@ -1033,6 +1034,7 @@ class AssistantsClient:
|
||||
*,
|
||||
metadata: Json = None,
|
||||
graph_id: str | None = None,
|
||||
name: str | None = None,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
sort_by: AssistantSortBy | None = None,
|
||||
@@ -1047,6 +1049,8 @@ class AssistantsClient:
|
||||
metadata: Metadata to filter by. Exact match filter for each KV pair.
|
||||
graph_id: The ID of the graph to filter by.
|
||||
The graph ID is normally set in your langgraph.json configuration.
|
||||
name: The name of the assistant to filter by.
|
||||
The filtering logic will match assistants where 'name' is a substring (case insensitive) of the assistant name.
|
||||
limit: The maximum number of results to return.
|
||||
offset: The number of results to skip.
|
||||
sort_by: The field to sort by.
|
||||
@@ -1077,6 +1081,8 @@ class AssistantsClient:
|
||||
payload["metadata"] = metadata
|
||||
if graph_id:
|
||||
payload["graph_id"] = graph_id
|
||||
if name:
|
||||
payload["name"] = name
|
||||
if sort_by:
|
||||
payload["sort_by"] = sort_by
|
||||
if sort_order:
|
||||
@@ -1876,7 +1882,7 @@ class RunsClient:
|
||||
thread_id: str,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -1906,7 +1912,7 @@ class RunsClient:
|
||||
thread_id: None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -1932,7 +1938,7 @@ class RunsClient:
|
||||
thread_id: str | None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -2091,7 +2097,7 @@ class RunsClient:
|
||||
thread_id: None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -2117,7 +2123,7 @@ class RunsClient:
|
||||
thread_id: str,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -2144,7 +2150,7 @@ class RunsClient:
|
||||
thread_id: str | None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -2346,7 +2352,7 @@ class RunsClient:
|
||||
thread_id: str,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
@@ -2373,7 +2379,7 @@ class RunsClient:
|
||||
thread_id: None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
@@ -2397,7 +2403,7 @@ class RunsClient:
|
||||
thread_id: str | None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
@@ -2870,7 +2876,7 @@ class CronClient:
|
||||
assistant_id: str,
|
||||
*,
|
||||
schedule: str,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
context: Context | None = None,
|
||||
@@ -2953,7 +2959,7 @@ class CronClient:
|
||||
assistant_id: str,
|
||||
*,
|
||||
schedule: str,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
context: Context | None = None,
|
||||
@@ -4289,6 +4295,7 @@ class SyncAssistantsClient:
|
||||
*,
|
||||
metadata: Json = None,
|
||||
graph_id: str | None = None,
|
||||
name: str | None = None,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
sort_by: AssistantSortBy | None = None,
|
||||
@@ -4303,6 +4310,8 @@ class SyncAssistantsClient:
|
||||
metadata: Metadata to filter by. Exact match filter for each KV pair.
|
||||
graph_id: The ID of the graph to filter by.
|
||||
The graph ID is normally set in your langgraph.json configuration.
|
||||
name: The name of the assistant to filter by.
|
||||
The filtering logic will match assistants where 'name' is a substring (case insensitive) of the assistant name.
|
||||
limit: The maximum number of results to return.
|
||||
offset: The number of results to skip.
|
||||
headers: Optional custom headers to include with the request.
|
||||
@@ -4330,6 +4339,8 @@ class SyncAssistantsClient:
|
||||
payload["metadata"] = metadata
|
||||
if graph_id:
|
||||
payload["graph_id"] = graph_id
|
||||
if name:
|
||||
payload["name"] = name
|
||||
if sort_by:
|
||||
payload["sort_by"] = sort_by
|
||||
if sort_order:
|
||||
@@ -5111,7 +5122,7 @@ class SyncRunsClient:
|
||||
thread_id: str,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -5140,7 +5151,7 @@ class SyncRunsClient:
|
||||
thread_id: None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -5167,7 +5178,7 @@ class SyncRunsClient:
|
||||
thread_id: str | None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -5322,7 +5333,7 @@ class SyncRunsClient:
|
||||
thread_id: None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -5348,7 +5359,7 @@ class SyncRunsClient:
|
||||
thread_id: str,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -5375,7 +5386,7 @@ class SyncRunsClient:
|
||||
thread_id: str | None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
stream_mode: StreamMode | Sequence[StreamMode] = "values",
|
||||
stream_subgraphs: bool = False,
|
||||
@@ -5577,7 +5588,7 @@ class SyncRunsClient:
|
||||
thread_id: str,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
@@ -5604,7 +5615,7 @@ class SyncRunsClient:
|
||||
thread_id: None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
@@ -5628,7 +5639,7 @@ class SyncRunsClient:
|
||||
thread_id: str | None,
|
||||
assistant_id: str,
|
||||
*,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
command: Command | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
@@ -6081,7 +6092,7 @@ class SyncCronClient:
|
||||
assistant_id: str,
|
||||
*,
|
||||
schedule: str,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
context: Context | None = None,
|
||||
@@ -6160,7 +6171,7 @@ class SyncCronClient:
|
||||
assistant_id: str,
|
||||
*,
|
||||
schedule: str,
|
||||
input: Mapping[str, Any] | None = None,
|
||||
input: Input | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
config: Config | None = None,
|
||||
context: Context | None = None,
|
||||
|
||||
@@ -3,15 +3,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping, Sequence
|
||||
from dataclasses import Field
|
||||
from datetime import datetime
|
||||
from typing import (
|
||||
Any,
|
||||
ClassVar,
|
||||
Literal,
|
||||
NamedTuple,
|
||||
Protocol,
|
||||
TypeAlias,
|
||||
TypedDict,
|
||||
Union,
|
||||
)
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
Json = dict[str, Any] | None
|
||||
"""Represents a JSON-like structure, which can be None or a dictionary with string keys and any values."""
|
||||
|
||||
@@ -143,8 +148,6 @@ SortOrder = Literal["asc", "desc"]
|
||||
The order to sort by.
|
||||
"""
|
||||
|
||||
Context: TypeAlias = dict[str, Any]
|
||||
|
||||
|
||||
class Config(TypedDict, total=False):
|
||||
"""Configuration options for a call."""
|
||||
@@ -413,6 +416,7 @@ CronSelectField = Literal[
|
||||
"next_run_date",
|
||||
"metadata",
|
||||
"now",
|
||||
"on_run_completed",
|
||||
]
|
||||
|
||||
PrimitiveData = str | int | float | bool | None
|
||||
@@ -558,3 +562,54 @@ class RunCreateMetadata(TypedDict):
|
||||
|
||||
thread_id: str | None
|
||||
"""The ID of the thread."""
|
||||
|
||||
|
||||
class _TypedDictLikeV1(Protocol):
|
||||
"""Protocol to represent types that behave like TypedDicts
|
||||
|
||||
Version 1: using `ClassVar` for keys."""
|
||||
|
||||
__required_keys__: ClassVar[frozenset[str]]
|
||||
__optional_keys__: ClassVar[frozenset[str]]
|
||||
|
||||
|
||||
class _TypedDictLikeV2(Protocol):
|
||||
"""Protocol to represent types that behave like TypedDicts
|
||||
|
||||
Version 2: not using `ClassVar` for keys."""
|
||||
|
||||
__required_keys__: frozenset[str]
|
||||
__optional_keys__: frozenset[str]
|
||||
|
||||
|
||||
class _DataclassLike(Protocol):
|
||||
"""Protocol to represent types that behave like dataclasses.
|
||||
|
||||
Inspired by the private _DataclassT from dataclasses that uses a similar protocol as a bound.
|
||||
"""
|
||||
|
||||
__dataclass_fields__: ClassVar[dict[str, Field[Any]]]
|
||||
|
||||
|
||||
class _BaseModelLike(Protocol):
|
||||
"""Protocol to represent types that behave like Pydantic `BaseModel`."""
|
||||
|
||||
model_config: ClassVar[dict[str, Any]]
|
||||
__pydantic_core_schema__: ClassVar[Any]
|
||||
|
||||
def model_dump(
|
||||
self,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]: ...
|
||||
|
||||
|
||||
_JSONLike: TypeAlias = None | str | int | float | bool
|
||||
_JSONMap: TypeAlias = Mapping[
|
||||
str, Union[_JSONLike, list[_JSONLike], "_JSONMap", list["_JSONMap"]]
|
||||
]
|
||||
|
||||
Input: TypeAlias = (
|
||||
_TypedDictLikeV1 | _TypedDictLikeV2 | _DataclassLike | _BaseModelLike | _JSONMap
|
||||
)
|
||||
|
||||
Context: TypeAlias = Input
|
||||
|
||||
@@ -23,7 +23,12 @@ Slack = "https://www.langchain.com/join-community"
|
||||
Reddit = "https://www.reddit.com/r/LangChain/"
|
||||
|
||||
[dependency-groups]
|
||||
test = ["pytest", "pytest-asyncio", "pytest-mock", "pytest-watch"]
|
||||
test = [
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-mock",
|
||||
"pytest-watch",
|
||||
]
|
||||
lint = ["ruff", "codespell", "mypy"]
|
||||
dev = [
|
||||
{ include-group = "test" },
|
||||
|
||||
@@ -30,6 +30,28 @@ async def test_serde_pydantic():
|
||||
assert result["name"] == "test"
|
||||
assert result["age"] == 25
|
||||
|
||||
nested_result = await _serde_roundtrip({"data": model})
|
||||
assert nested_result["data"]["name"] == "test"
|
||||
assert nested_result["data"]["age"] == 25
|
||||
|
||||
|
||||
async def test_serde_dataclass():
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class TestDataClass:
|
||||
name: str
|
||||
age: int
|
||||
|
||||
data = TestDataClass(name="test", age=25)
|
||||
result = await _serde_roundtrip(data)
|
||||
assert result["name"] == "test"
|
||||
assert result["age"] == 25
|
||||
|
||||
nested_result = await _serde_roundtrip({"data": data})
|
||||
assert nested_result["data"]["name"] == "test"
|
||||
assert nested_result["data"]["age"] == 25
|
||||
|
||||
|
||||
async def test_serde_pydantic_cls_fails():
|
||||
# Test that serialization fails gracefully for Pydantic model when not available
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import runtime_checkable
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langgraph_sdk.schema import (
|
||||
_BaseModelLike,
|
||||
_DataclassLike,
|
||||
)
|
||||
|
||||
|
||||
def rc(cls: type) -> type:
|
||||
return runtime_checkable(cls)
|
||||
|
||||
|
||||
class MyModel(BaseModel):
|
||||
foo: str
|
||||
|
||||
|
||||
def test_base_model_like():
|
||||
assert isinstance(MyModel(foo="test"), rc(_BaseModelLike))
|
||||
|
||||
|
||||
@dataclass
|
||||
class MyDataclass:
|
||||
foo: str
|
||||
|
||||
|
||||
def test_dataclass_like():
|
||||
assert isinstance(MyDataclass(foo="test"), rc(_DataclassLike))
|
||||
Generated
+56
-56
@@ -70,14 +70,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf57
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -197,7 +197,7 @@ test = [
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.18.2"
|
||||
version = "1.17.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mypy-extensions" },
|
||||
@@ -205,39 +205,39 @@ dependencies = [
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/03/6f/657961a0743cff32e6c0611b63ff1c1970a0b482ace35b069203bf705187/mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c", size = 12807973, upload-time = "2025-09-19T00:10:35.282Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/e9/420822d4f661f13ca8900f5fa239b40ee3be8b62b32f3357df9a3045a08b/mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e", size = 11896527, upload-time = "2025-09-19T00:10:55.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/73/a05b2bbaa7005f4642fcfe40fb73f2b4fb6bb44229bd585b5878e9a87ef8/mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b", size = 12507004, upload-time = "2025-09-19T00:11:05.411Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/01/f6e4b9f0d031c11ccbd6f17da26564f3a0f3c4155af344006434b0a05a9d/mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66", size = 13245947, upload-time = "2025-09-19T00:10:46.923Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/97/19727e7499bfa1ae0773d06afd30ac66a58ed7437d940c70548634b24185/mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428", size = 13499217, upload-time = "2025-09-19T00:09:39.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/4f/90dc8c15c1441bf31cf0f9918bb077e452618708199e530f4cbd5cede6ff/mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed", size = 9766753, upload-time = "2025-09-19T00:10:49.161Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -557,28 +557,28 @@ sdist = { url = "https://files.pythonhosted.org/packages/36/47/ab65fc1d682befc31
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.14.5"
|
||||
version = "0.14.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/fa/fbb67a5780ae0f704876cb8ac92d6d76da41da4dc72b7ed3565ab18f2f52/ruff-0.14.5.tar.gz", hash = "sha256:8d3b48d7d8aad423d3137af7ab6c8b1e38e4de104800f0d596990f6ada1a9fc1", size = 5615944, upload-time = "2025-11-13T19:58:51.155Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/68/31/c07e9c535248d10836a94e4f4e8c5a31a1beed6f169b31405b227872d4f4/ruff-0.14.5-py3-none-linux_armv6l.whl", hash = "sha256:f3b8248123b586de44a8018bcc9fefe31d23dda57a34e6f0e1e53bd51fd63594", size = 13171630, upload-time = "2025-11-13T19:57:54.894Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/5c/283c62516dca697cd604c2796d1487396b7a436b2f0ecc3fd412aca470e0/ruff-0.14.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f7a75236570318c7a30edd7f5491945f0169de738d945ca8784500b517163a72", size = 13413925, upload-time = "2025-11-13T19:57:59.181Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/f3/aa319f4afc22cb6fcba2b9cdfc0f03bbf747e59ab7a8c5e90173857a1361/ruff-0.14.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d146132d1ee115f8802356a2dc9a634dbf58184c51bff21f313e8cd1c74899a", size = 12574040, upload-time = "2025-11-13T19:58:02.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/7f/cb5845fcc7c7e88ed57f58670189fc2ff517fe2134c3821e77e29fd3b0c8/ruff-0.14.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2380596653dcd20b057794d55681571a257a42327da8894b93bbd6111aa801f", size = 13009755, upload-time = "2025-11-13T19:58:05.172Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/d2/bcbedbb6bcb9253085981730687ddc0cc7b2e18e8dc13cf4453de905d7a0/ruff-0.14.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d1fa985a42b1f075a098fa1ab9d472b712bdb17ad87a8ec86e45e7fa6273e68", size = 12937641, upload-time = "2025-11-13T19:58:08.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/58/e25de28a572bdd60ffc6bb71fc7fd25a94ec6a076942e372437649cbb02a/ruff-0.14.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88f0770d42b7fa02bbefddde15d235ca3aa24e2f0137388cc15b2dcbb1f7c7a7", size = 13610854, upload-time = "2025-11-13T19:58:11.419Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/24/43bb3fd23ecee9861970978ea1a7a63e12a204d319248a7e8af539984280/ruff-0.14.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3676cb02b9061fee7294661071c4709fa21419ea9176087cb77e64410926eb78", size = 15061088, upload-time = "2025-11-13T19:58:14.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/44/a022f288d61c2f8c8645b24c364b719aee293ffc7d633a2ca4d116b9c716/ruff-0.14.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b595bedf6bc9cab647c4a173a61acf4f1ac5f2b545203ba82f30fcb10b0318fb", size = 14734717, upload-time = "2025-11-13T19:58:17.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/81/5c6ba44de7e44c91f68073e0658109d8373b0590940efe5bd7753a2585a3/ruff-0.14.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f55382725ad0bdb2e8ee2babcbbfb16f124f5a59496a2f6a46f1d9d99d93e6e2", size = 14028812, upload-time = "2025-11-13T19:58:20.533Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/ef/41a8b60f8462cb320f68615b00299ebb12660097c952c600c762078420f8/ruff-0.14.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7497d19dce23976bdaca24345ae131a1d38dcfe1b0850ad8e9e6e4fa321a6e19", size = 13825656, upload-time = "2025-11-13T19:58:23.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/00/207e5de737fdb59b39eb1fac806904fe05681981b46d6a6db9468501062e/ruff-0.14.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:410e781f1122d6be4f446981dd479470af86537fb0b8857f27a6e872f65a38e4", size = 13959922, upload-time = "2025-11-13T19:58:26.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/7e/fa1f5c2776db4be405040293618846a2dece5c70b050874c2d1f10f24776/ruff-0.14.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01be527ef4c91a6d55e53b337bfe2c0f82af024cc1a33c44792d6844e2331e1", size = 12932501, upload-time = "2025-11-13T19:58:29.822Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/d8/d86bf784d693a764b59479a6bbdc9515ae42c340a5dc5ab1dabef847bfaa/ruff-0.14.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f66e9bb762e68d66e48550b59c74314168ebb46199886c5c5aa0b0fbcc81b151", size = 12927319, upload-time = "2025-11-13T19:58:32.923Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/de/ee0b304d450ae007ce0cb3e455fe24fbcaaedae4ebaad6c23831c6663651/ruff-0.14.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d93be8f1fa01022337f1f8f3bcaa7ffee2d0b03f00922c45c2207954f351f465", size = 13206209, upload-time = "2025-11-13T19:58:35.952Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/aa/193ca7e3a92d74f17d9d5771a765965d2cf42c86e6f0fd95b13969115723/ruff-0.14.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c135d4b681f7401fe0e7312017e41aba9b3160861105726b76cfa14bc25aa367", size = 13953709, upload-time = "2025-11-13T19:58:39.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/f1/7119e42aa1d3bf036ffc9478885c2e248812b7de9abea4eae89163d2929d/ruff-0.14.5-py3-none-win32.whl", hash = "sha256:c83642e6fccfb6dea8b785eb9f456800dcd6a63f362238af5fc0c83d027dd08b", size = 12925808, upload-time = "2025-11-13T19:58:42.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/9d/7c0a255d21e0912114784e4a96bf62af0618e2190cae468cd82b13625ad2/ruff-0.14.5-py3-none-win_amd64.whl", hash = "sha256:9d55d7af7166f143c94eae1db3312f9ea8f95a4defef1979ed516dbb38c27621", size = 14331546, upload-time = "2025-11-13T19:58:45.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/80/69756670caedcf3b9be597a6e12276a6cf6197076eb62aad0c608f8efce0/ruff-0.14.5-py3-none-win_arm64.whl", hash = "sha256:4b700459d4649e2594b31f20a9de33bc7c19976d4746d8d0798ad959621d64a4", size = 13433331, upload-time = "2025-11-13T19:58:48.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user